diff --git a/src/main/kotlin/moe/kageru/kagebot/Command.kt b/src/main/kotlin/moe/kageru/kagebot/Command.kt deleted file mode 100644 index 4b39210..0000000 --- a/src/main/kotlin/moe/kageru/kagebot/Command.kt +++ /dev/null @@ -1,79 +0,0 @@ -package moe.kageru.kagebot - -import com.google.gson.annotations.SerializedName -import moe.kageru.kagebot.Config.Companion.config -import moe.kageru.kagebot.Util.doIf -import org.javacord.api.entity.message.MessageAuthor -import org.javacord.api.event.message.MessageCreateEvent - -private const val AUTHOR_PLACEHOLDER = "@@" - -class Command( - trigger: String?, - private val response: String?, - matchType: MatchType?, - private val permissions: Permissions?, - @SerializedName("action") private val actions: MessageActions? -) { - val trigger: String = trigger!! - val regex: Regex? = if (matchType == MatchType.REGEX) Regex(trigger!!) else null - val matchType: MatchType = matchType ?: MatchType.PREFIX - - constructor(cmd: Command) : this( - cmd.trigger, - cmd.response, - cmd.matchType, - cmd.permissions?.let { Permissions(it) }, - cmd.actions - ) - - fun execute(message: MessageCreateEvent) { - if (!(message.messageAuthor.isBotOwner || permissions?.isAllowed(message) != false)) { - message.channel.sendMessage(config.localization.permissionDenied) - return - } - this.actions?.run(message, this) - this.response?.let { - message.channel.sendMessage(respond(message.messageAuthor)) - } - } - - fun matches(msg: String) = this.matchType.matches(msg, this) - private fun respond(author: MessageAuthor) = this.response!!.doIf({ it.contains(AUTHOR_PLACEHOLDER) }) { - it.replace(AUTHOR_PLACEHOLDER, MessageUtil.mention(author)) - } -} - -enum class MatchType { - PREFIX { - override fun matches(message: String, command: Command) = message.startsWith(command.trigger) - }, - CONTAINS { - override fun matches(message: String, command: Command) = message.contains(command.trigger) - }, - REGEX { - override fun matches(message: String, command: Command) = command.regex!!.matches(message) - }; - - abstract fun matches(message: String, command: Command): Boolean -} - -class Permissions(hasOneOf: Iterable?, hasNoneOf: Iterable?, private val onlyDM: Boolean) { - private val hasNoneOf = hasNoneOf?.toSet() - private val hasOneOf = hasOneOf?.toSet() - - constructor(perms: Permissions) : this(perms.hasOneOf, perms.hasNoneOf, perms.onlyDM) - - fun isAllowed(message: MessageCreateEvent): Boolean { - if (onlyDM && !message.isPrivateMessage) { - return false - } - hasOneOf?.let { - if (!Util.hasOneOf(message.messageAuthor, hasOneOf)) return false - } - hasNoneOf?.let { - if (Util.hasOneOf(message.messageAuthor, hasNoneOf)) return false - } - return true - } -} \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/Config.kt b/src/main/kotlin/moe/kageru/kagebot/Config.kt deleted file mode 100644 index 10befd5..0000000 --- a/src/main/kotlin/moe/kageru/kagebot/Config.kt +++ /dev/null @@ -1,43 +0,0 @@ -package moe.kageru.kagebot - -import com.google.gson.annotations.SerializedName -import com.moandjiezana.toml.Toml -import org.javacord.api.DiscordApi -import org.javacord.api.entity.server.Server -import java.io.File - -class Config( - val system: System, - val localization: Localization, - @SerializedName("command") val commands: List -) { - companion object { - val config: Config by lazy { read("config.toml") } - 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 { - val file = File(path) - if (file.isFile) { - return@run file - } - println("Config not found, falling back to defaults...") - File(this::class.java.classLoader.getResource(path)!!.toURI()) - }) - val parsed = rawConfig.to(Config::class.java) - return Config( - parsed.system, - parsed.localization, - parsed.commands.map { Command(it) } - ) - } - - } -} - -data class System(val serverId: String, val color: String) -data class Localization(val permissionDenied: String, val redirectedMessage: String, val messageDeleted: String) \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/Globals.kt b/src/main/kotlin/moe/kageru/kagebot/Globals.kt new file mode 100644 index 0000000..b2be09b --- /dev/null +++ b/src/main/kotlin/moe/kageru/kagebot/Globals.kt @@ -0,0 +1,11 @@ +package moe.kageru.kagebot + +import moe.kageru.kagebot.config.Config +import org.javacord.api.DiscordApi +import org.javacord.api.entity.server.Server + +object Globals { + lateinit var server: Server + lateinit var api: DiscordApi + lateinit var config: Config +} \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/Kagebot.kt b/src/main/kotlin/moe/kageru/kagebot/Kagebot.kt index 2ccdf3e..4560b0b 100644 --- a/src/main/kotlin/moe/kageru/kagebot/Kagebot.kt +++ b/src/main/kotlin/moe/kageru/kagebot/Kagebot.kt @@ -1,9 +1,12 @@ package moe.kageru.kagebot -import moe.kageru.kagebot.Config.Companion.config import moe.kageru.kagebot.Log.log +import moe.kageru.kagebot.config.Config +import moe.kageru.kagebot.config.RawConfig import org.javacord.api.DiscordApiBuilder import org.javacord.api.event.message.MessageCreateEvent +import org.javacord.api.event.server.member.ServerMemberJoinEvent +import java.io.File class Kagebot { companion object { @@ -11,24 +14,53 @@ class Kagebot { if (event.messageAuthor.isYourself) { return } - for (command in config.commands) { + for (command in Globals.config.commands) { if (command.matches(event.messageContent)) { command.execute(event) break } } } + + fun welcomeUser(event: ServerMemberJoinEvent) { + Globals.config.features.welcome!!.let { welcome -> + val message = event.user.sendMessage(welcome.embed) + // If the user disabled direct messages, try the fallback (if defined) + if (message.isCompletedExceptionally + && welcome.fallbackChannel != null + && welcome.fallbackMessage != null + ) { + welcome.fallbackChannel.sendMessage( + welcome.fallbackMessage.replace( + "@@", + MessageUtil.mention(event.user) + ) + ) + } + } + } } init { - val api = DiscordApiBuilder().setToken(Config.secret).login().join() - Config.server = api.getServerById(config.system.serverId).orElseThrow() - Config.api = api + Globals.api = DiscordApiBuilder().setToken(getSecret()).login().join() + try { + Globals.config = Config(RawConfig.read()) + } catch (e: IllegalArgumentException) { + println("Config error:\n$e,\n${e.message},\n${e.stackTrace}") + System.exit(1) + } Runtime.getRuntime().addShutdownHook(Thread { log.info("Bot has been interrupted. Shutting down.") - api.disconnect() + Globals.api.disconnect() }) log.info("kagebot Mk II running") - api.addMessageCreateListener { processMessage(it) } + Globals.api.addMessageCreateListener { processMessage(it) } + Globals.config.features.welcome?.let { welcome -> + if (welcome.enabled) { + Globals.api.addServerMemberJoinListener { welcomeUser(it) } + } + } } -} \ No newline at end of file + + private fun getSecret() = File("secret").readText().replace("\n", "") +} diff --git a/src/main/kotlin/moe/kageru/kagebot/MessageUtil.kt b/src/main/kotlin/moe/kageru/kagebot/MessageUtil.kt index 9f95df8..dd7bc5c 100644 --- a/src/main/kotlin/moe/kageru/kagebot/MessageUtil.kt +++ b/src/main/kotlin/moe/kageru/kagebot/MessageUtil.kt @@ -1,18 +1,30 @@ package moe.kageru.kagebot -import moe.kageru.kagebot.Config.Companion.config import org.javacord.api.entity.message.MessageAuthor import org.javacord.api.entity.message.embed.EmbedBuilder -import java.awt.Color +import org.javacord.api.entity.user.User object MessageUtil { fun mention(user: MessageAuthor): String { return "<@${user.id}>" } + fun mention(user: User): String { + return "<@${user.id}>" + } + + fun getEmbedBuilder(): EmbedBuilder { val builder = EmbedBuilder() - Config.server!!.icon.ifPresent { builder.setThumbnail(it) } - return builder.setColor(Color.decode(config.system.color)).setTimestampToNow() + Globals.server.icon.ifPresent { builder.setThumbnail(it) } + return builder.setColor(Globals.config.system.color).setTimestampToNow() + } + + fun mapToEmbed(contents: Map): EmbedBuilder { + val builder = getEmbedBuilder() + for ((heading, content) in contents) { + builder.addField(heading, content) + } + return builder } } \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/Util.kt b/src/main/kotlin/moe/kageru/kagebot/Util.kt index 8a25ef1..e6d268c 100644 --- a/src/main/kotlin/moe/kageru/kagebot/Util.kt +++ b/src/main/kotlin/moe/kageru/kagebot/Util.kt @@ -1,6 +1,10 @@ package moe.kageru.kagebot +import moe.kageru.kagebot.Globals.api +import moe.kageru.kagebot.Globals.server +import org.javacord.api.entity.channel.TextChannel import org.javacord.api.entity.message.MessageAuthor +import org.javacord.api.entity.permission.Role import java.util.* object Util { @@ -8,6 +12,10 @@ object Util { return if (condition(this)) op(this) else this } + /** + * Mimics the behavior of [Optional.ifPresent], but returns null if the optional is empty, + * allowing easier fallback behavior via Kotlin’s ?: operator. + */ inline fun Optional.ifNotEmpty(op: (T) -> R): R? { if (this.isPresent) { return op(this.get()) @@ -15,9 +23,50 @@ object Util { return null } - fun hasOneOf(messageAuthor: MessageAuthor, roles: Set): Boolean { + fun hasOneOf(messageAuthor: MessageAuthor, roles: Set): Boolean { return messageAuthor.asUser().ifNotEmpty { user -> - user.getRoles(Config.server).map { it.id }.toSet().intersect(roles).isNotEmpty() + user.getRoles(server).map { it }.toSet().intersect(roles).isNotEmpty() } ?: false } + + private val channelIdRegex = Regex("\\d{18}") + fun String.isEntityId() = channelIdRegex.matches(this) + + @Throws(IllegalArgumentException::class) + fun findRole(idOrName: String): Role { + return when { + idOrName.isEntityId() -> server.getRoleById(idOrName).ifNotEmpty { it } + ?: throw IllegalArgumentException("Role $idOrName not found.") + else -> server.getRolesByNameIgnoreCase(idOrName).let { + when (it.size) { + 0 -> throw IllegalArgumentException("Role $idOrName not found.") + 1 -> it[0] + else -> throw IllegalArgumentException("More than one role found with name $idOrName. Please specify the role ID instead") + } + } + } + } + + @Throws(IllegalArgumentException::class) + fun findChannel(idOrName: String): TextChannel { + return when { + idOrName.isEntityId() -> server.getTextChannelById(idOrName).ifNotEmpty { it } + ?: throw IllegalArgumentException("Channel ID $idOrName not found.") + else -> if (idOrName.startsWith('@')) { + api.getCachedUserByDiscriminatedName(idOrName.removePrefix("@")).ifNotEmpty { user -> + user.privateChannel.ifNotEmpty { it } + ?: throw IllegalArgumentException("Could not open private channel with user $idOrName for redirection.") + } + ?: throw IllegalArgumentException("Can’t find user $idOrName for redirection.") + } else { + server.getTextChannelsByName(idOrName).let { + when (it.size) { + 0 -> throw IllegalArgumentException("Channel $idOrName not found.") + 1 -> it[0] + else -> throw IllegalArgumentException("More than one channel found with name $idOrName. Please specify the channel ID instead") + } + } + } + } + } } \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/command/Command.kt b/src/main/kotlin/moe/kageru/kagebot/command/Command.kt new file mode 100644 index 0000000..e28c8b2 --- /dev/null +++ b/src/main/kotlin/moe/kageru/kagebot/command/Command.kt @@ -0,0 +1,62 @@ +package moe.kageru.kagebot.command + +import moe.kageru.kagebot.Globals.config +import moe.kageru.kagebot.MessageUtil +import moe.kageru.kagebot.Util.doIf +import moe.kageru.kagebot.config.RawCommand +import org.javacord.api.entity.message.MessageAuthor +import org.javacord.api.event.message.MessageCreateEvent + +private const val AUTHOR_PLACEHOLDER = "@@" + +class Command(cmd: RawCommand) { + val trigger: String + private val response: String? + val matchType: MatchType + private val permissions: Permissions? + private val actions: MessageActions? + val regex: Regex? + + init { + trigger = cmd.trigger!! + response = cmd.response + matchType = cmd.matchType?.let { type -> + MatchType.values().find { it.name.equals(type, ignoreCase = true) } + ?: throw IllegalArgumentException("Invalid [command.matchType]: “${cmd.matchType}”") + } ?: MatchType.PREFIX + permissions = cmd.permissions?.let { Permissions(it) } + actions = cmd.actions?.let { MessageActions(it) } + regex = if (matchType == MatchType.REGEX) Regex(trigger) else null + } + + fun execute(message: MessageCreateEvent) { + if (permissions?.isAllowed(message) == false) { + message.channel.sendMessage(config.localization.permissionDenied) + return + } + this.actions?.run(message, this) + this.response?.let { + message.channel.sendMessage(respond(message.messageAuthor)) + } + } + + fun matches(msg: String) = this.matchType.matches(msg, this) + private fun respond(author: MessageAuthor) = this.response!!.doIf({ it.contains(AUTHOR_PLACEHOLDER) }) { + it.replace(AUTHOR_PLACEHOLDER, MessageUtil.mention(author)) + } +} + +enum class MatchType { + PREFIX { + override fun matches(message: String, command: Command) = message.startsWith(command.trigger) + }, + CONTAINS { + override fun matches(message: String, command: Command) = message.contains(command.trigger) + }, + REGEX { + override fun matches(message: String, command: Command) = command.regex!!.matches(message) + }; + + abstract fun matches(message: String, command: Command): Boolean +} + diff --git a/src/main/kotlin/moe/kageru/kagebot/MessageActions.kt b/src/main/kotlin/moe/kageru/kagebot/command/MessageActions.kt similarity index 51% rename from src/main/kotlin/moe/kageru/kagebot/MessageActions.kt rename to src/main/kotlin/moe/kageru/kagebot/command/MessageActions.kt index a0d8eba..ced5954 100644 --- a/src/main/kotlin/moe/kageru/kagebot/MessageActions.kt +++ b/src/main/kotlin/moe/kageru/kagebot/command/MessageActions.kt @@ -1,27 +1,45 @@ -package moe.kageru.kagebot +package moe.kageru.kagebot.command -import moe.kageru.kagebot.Config.Companion.config +import moe.kageru.kagebot.Globals.config import moe.kageru.kagebot.Log.log -import moe.kageru.kagebot.Util.ifNotEmpty +import moe.kageru.kagebot.MessageUtil +import moe.kageru.kagebot.Util +import moe.kageru.kagebot.config.RawMessageActions +import moe.kageru.kagebot.config.RawRedirect +import org.javacord.api.entity.channel.TextChannel import org.javacord.api.event.message.MessageCreateEvent -class MessageActions(private val delete: Boolean, private val redirect: Redirect?) { +class MessageActions(rawActions: RawMessageActions) { + private val delete: Boolean = rawActions.delete + private val redirect: Redirect? = rawActions.redirect?.let { Redirect(it) } + fun run(message: MessageCreateEvent, command: Command) { - if (delete && message.message.canYouDelete()) { + if (delete) { + deleteMessage(message) + } + redirect?.execute(message, command) + } + + private fun deleteMessage(message: MessageCreateEvent) { + if (message.message.canYouDelete()) { message.deleteMessage() - message.messageAuthor.asUser().ifNotEmpty { user -> + message.messageAuthor.asUser().ifPresent { user -> user.sendMessage( MessageUtil.getEmbedBuilder() .addField("Blacklisted", config.localization.messageDeleted) .addField("Original:", "“${message.readableMessageContent}”") ) } + } else { + log.info("Tried to delete a message without the necessary permissions. Channel: ${message.channel.id}") } - redirect?.execute(message, command) } } -class Redirect(private val target: Long, private val anonymous: Boolean) { +class Redirect(rawRedirect: RawRedirect) { + private val target: TextChannel = rawRedirect.target?.let(Util::findChannel) + ?: throw IllegalArgumentException("Every redirect needs to have a target.") + private val anonymous: Boolean = rawRedirect.anonymous fun execute(message: MessageCreateEvent, command: Command) { val embed = MessageUtil.getEmbedBuilder() @@ -43,7 +61,9 @@ class Redirect(private val target: Long, private val anonymous: Boolean) { setAuthor(message.messageAuthor) } } - Config.server!!.getTextChannelById(target).ifNotEmpty { it.sendMessage(embed) } - ?: log.warning("Could not redirect message to channel $target") + + if (target.sendMessage(embed).isCompletedExceptionally) { + log.warning("Could not redirect message to channel $target") + } } } \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/command/Permissions.kt b/src/main/kotlin/moe/kageru/kagebot/command/Permissions.kt new file mode 100644 index 0000000..7052b9c --- /dev/null +++ b/src/main/kotlin/moe/kageru/kagebot/command/Permissions.kt @@ -0,0 +1,34 @@ +package moe.kageru.kagebot.command + +import moe.kageru.kagebot.Util +import moe.kageru.kagebot.config.RawPermissions +import org.javacord.api.entity.permission.Role +import org.javacord.api.event.message.MessageCreateEvent + +class Permissions(perms: RawPermissions) { + private val hasOneOf: Set? + private val hasNoneOf: Set? + private val onlyDM: Boolean + + init { + hasOneOf = perms.hasOneOf?.mapTo(mutableSetOf(), Util::findRole) + hasNoneOf = perms.hasNoneOf?.mapTo(mutableSetOf(), Util::findRole) + onlyDM = perms.onlyDM + } + + fun isAllowed(message: MessageCreateEvent): Boolean { + if (message.messageAuthor.isBotOwner) { + return true + } + if (onlyDM && !message.isPrivateMessage) { + return false + } + hasOneOf?.let { roles -> + if (!Util.hasOneOf(message.messageAuthor, roles)) return false + } + hasNoneOf?.let { roles -> + if (Util.hasOneOf(message.messageAuthor, roles)) return false + } + return true + } +} diff --git a/src/main/kotlin/moe/kageru/kagebot/config/Config.kt b/src/main/kotlin/moe/kageru/kagebot/config/Config.kt new file mode 100644 index 0000000..e0333be --- /dev/null +++ b/src/main/kotlin/moe/kageru/kagebot/config/Config.kt @@ -0,0 +1,47 @@ +package moe.kageru.kagebot.config + +import moe.kageru.kagebot.Globals +import moe.kageru.kagebot.Globals.api +import moe.kageru.kagebot.command.Command +import moe.kageru.kagebot.features.Features +import moe.kageru.kagebot.features.WelcomeFeature +import java.awt.Color +import kotlin.IllegalArgumentException + +class Config(rawConfig: RawConfig) { + val system: SystemConfig + val localization: Localization + val commands: List + val features: Features + + init { + system = rawConfig.system?.let { + SystemConfig( + it.serverId ?: throw IllegalArgumentException("No [system.server] defined."), + Color.decode(it.color ?: "#1793d0") + ) + } ?: throw IllegalArgumentException("No [system] block in config.") + Globals.server = api.getServerById(system.serverId).orElseThrow() + + localization = rawConfig.localization?.let { + Localization( + permissionDenied = it.permissionDenied + ?: throw IllegalArgumentException("No [localization.permissionDenied] defined"), + redirectedMessage = it.redirectedMessage + ?: throw IllegalArgumentException("No [localization.permissionDenied] defined"), + messageDeleted = it.messageDeleted + ?: throw IllegalArgumentException("No [localization.permissionDenied] defined") + ) + } ?: throw IllegalArgumentException("No [localization] block in config.") + + commands = rawConfig.commands?.let { rawCommands -> + rawCommands.map { Command(it) } + } ?: emptyList() + + features = rawConfig.features?.let { Features(it) } + ?: throw IllegalArgumentException("No [feature] block in config.") + } +} + +class SystemConfig(val serverId: String, val color: Color) +class Localization(val permissionDenied: String, val redirectedMessage: String, val messageDeleted: String) diff --git a/src/main/kotlin/moe/kageru/kagebot/config/RawConfig.kt b/src/main/kotlin/moe/kageru/kagebot/config/RawConfig.kt new file mode 100644 index 0000000..23fd423 --- /dev/null +++ b/src/main/kotlin/moe/kageru/kagebot/config/RawConfig.kt @@ -0,0 +1,50 @@ +package moe.kageru.kagebot.config + +import com.google.gson.annotations.SerializedName +import com.moandjiezana.toml.Toml +import java.io.File + +class RawConfig( + val system: RawSystemConfig?, + val localization: RawLocalization?, + @SerializedName("command") val commands: List?, + @SerializedName("feature") val features: RawFeatures? +) { + companion object { + const val DEFAULT_CONFIG_PATH = "config.toml" + + fun read(path: String = DEFAULT_CONFIG_PATH): RawConfig { + val toml: Toml = Toml().read(run { + val file = File(path) + if (file.isFile) { + return@run file + } + println("Config not found, falling back to defaults...") + File(this::class.java.classLoader.getResource(path)!!.toURI()) + }) + return toml.to(RawConfig::class.java) + } + + } +} + +class RawSystemConfig(val serverId: String?, val color: String?) +class RawLocalization(val permissionDenied: String?, val redirectedMessage: String?, val messageDeleted: String?) +class RawCommand( + val trigger: String?, + val response: String?, + val matchType: String?, + val permissions: RawPermissions?, + @SerializedName("action") val actions: RawMessageActions? +) + +class RawPermissions(val hasOneOf: List?, val hasNoneOf: List?, val onlyDM: Boolean) +class RawMessageActions(val delete: Boolean, val redirect: RawRedirect?) +class RawRedirect(val target: String?, val anonymous: Boolean) +class RawFeatures(val welcome: RawWelcomeFeature?) +class RawWelcomeFeature( + val enabled: Boolean, + val content: Map?, + val fallbackChannel: String?, + val fallbackMessage: String? +) \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/features/Features.kt b/src/main/kotlin/moe/kageru/kagebot/features/Features.kt new file mode 100644 index 0000000..3927f96 --- /dev/null +++ b/src/main/kotlin/moe/kageru/kagebot/features/Features.kt @@ -0,0 +1,27 @@ +package moe.kageru.kagebot.features + +import moe.kageru.kagebot.MessageUtil +import moe.kageru.kagebot.Util +import moe.kageru.kagebot.config.RawFeatures +import moe.kageru.kagebot.config.RawWelcomeFeature +import org.javacord.api.entity.channel.TextChannel +import org.javacord.api.entity.message.embed.EmbedBuilder + +class Features(rawFeatures: RawFeatures) { + val welcome: WelcomeFeature? = rawFeatures.welcome?.let { WelcomeFeature(it) } +} + +class WelcomeFeature(rawWelcome: RawWelcomeFeature) { + val enabled: Boolean = rawWelcome.enabled + val embed: EmbedBuilder? by lazy { + rawWelcome.content?.let(MessageUtil::mapToEmbed) + } + val fallbackChannel: TextChannel? = rawWelcome.fallbackChannel?.let { + if (rawWelcome.fallbackMessage == null) { + throw IllegalArgumentException("[feature.welcome.fallbackMessage] must not be null if fallbackChannel is defined") + } + Util.findChannel(it) + } + val fallbackMessage: String? = rawWelcome.fallbackMessage + +} diff --git a/src/main/kotlin/moe/kageru/kagebot/main.kt b/src/main/kotlin/moe/kageru/kagebot/main.kt index 9b1026f..7d3f7bb 100644 --- a/src/main/kotlin/moe/kageru/kagebot/main.kt +++ b/src/main/kotlin/moe/kageru/kagebot/main.kt @@ -4,10 +4,10 @@ import moe.kageru.kagebot.Log.log import java.lang.System fun main() { - try { + //try { Kagebot() - } catch (e: Exception) { + /*} catch (e: Exception) { log.warning("An exception occurred in the main thread, exiting.\n${e.stackTrace.joinToString("\n")}") System.exit(1) - } + }*/ } diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml index d46821c..a7f528e 100644 --- a/src/main/resources/config.toml +++ b/src/main/resources/config.toml @@ -8,6 +8,19 @@ permissionDenied = "You do not have permission to use this command." redirectedMessage = "says" messageDeleted = "Your message was deleted because it contained a banned word or phrase." +# If this is enabled, every new user will receive a welcome message. +# If the user has disabled their DMs, the fallbackMessage will be sent in the fallbackChannel instead. +# If no fallback channel or message is specified, no fallback will be sent. +[feature.welcome] +enabled = true +fallbackChannel = 555097559023222825 +fallbackMessage = "@@ I would like to greet you, but I can’t. :(" +# This is a list of pairs where the key is the title and the value the content of the paragraph. +# Do not use empty strings to get empty headings or paragraphs. The discord API rejects those. +[feature.welcome.content] +"Welcome to the Server" = "This is the content of the first paragraph" +"Second paragraph heading" = "Second paragraph content" + [[command]] trigger = "!ping" response = "pong" @@ -38,29 +51,27 @@ trigger = "!restricted" response = "access granted" [command.permissions] hasOneOf = [ - 452034011393425409, - 446668543816106004 + "452034011393425409", + "446668543816106004" ] [[command]] trigger = "!almostUnrestricted" response = "access granted" [command.permissions] -hasNoneOf = [452034011393425409] - - +hasNoneOf = ["452034011393425409"] # redirect every message that starts with !redirect to channel 555097559023222825 [[command]] trigger = "!redirect" response = "redirected" [command.action.redirect] -target = 555097559023222825 +target = "testchannel" # the same, but without the original username [[command]] trigger = "!anonRedirect" response = "redirected" [command.action.redirect] -target = 555097559023222825 +target = "555097559023222825" anonymous = true diff --git a/src/test/kotlin/moe/kageru/kagebot/CommandTest.kt b/src/test/kotlin/moe/kageru/kagebot/CommandTest.kt index f488daf..bab8142 100644 --- a/src/test/kotlin/moe/kageru/kagebot/CommandTest.kt +++ b/src/test/kotlin/moe/kageru/kagebot/CommandTest.kt @@ -6,11 +6,11 @@ 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 moe.kageru.kagebot.TestUtil.embedToString import moe.kageru.kagebot.TestUtil.messageableAuthor import moe.kageru.kagebot.TestUtil.mockMessage import moe.kageru.kagebot.TestUtil.testMessageSuccess +import moe.kageru.kagebot.config.RawConfig.Companion.config import org.javacord.api.entity.message.embed.EmbedBuilder class CommandTest : StringSpec({ diff --git a/src/test/kotlin/moe/kageru/kagebot/ConfigTest.kt b/src/test/kotlin/moe/kageru/kagebot/ConfigTest.kt index f7fbe89..6bb398d 100644 --- a/src/test/kotlin/moe/kageru/kagebot/ConfigTest.kt +++ b/src/test/kotlin/moe/kageru/kagebot/ConfigTest.kt @@ -2,10 +2,16 @@ package moe.kageru.kagebot import io.kotlintest.shouldNotBe import io.kotlintest.specs.StringSpec +import moe.kageru.kagebot.config.RawConfig class ConfigTest : StringSpec({ + /* "should properly parse default config" { Config.config shouldNotBe null Config.config.commands shouldNotBe null } + */ + "should convert to raw config" { + RawConfig.config shouldNotBe null + } }) \ No newline at end of file diff --git a/src/test/kotlin/moe/kageru/kagebot/FeatureTest.kt b/src/test/kotlin/moe/kageru/kagebot/FeatureTest.kt new file mode 100644 index 0000000..71ccf0c --- /dev/null +++ b/src/test/kotlin/moe/kageru/kagebot/FeatureTest.kt @@ -0,0 +1,45 @@ +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.slot +import org.javacord.api.entity.message.embed.EmbedBuilder +class FeatureTest +/* +class FeatureTest : StringSpec({ + "should send welcome" { + val sentMessages = mutableListOf() + Kagebot.welcomeUser( + mockk { + every { user } returns mockk { + every { id } returns 123 + every { sendMessage(capture(sentMessages)) } + } + } + ) + sentMessages.size shouldBe 1 + TestUtil.embedToString(sentMessages[0]).let { embed -> + Config.config.features!!.welcome!!.content!!.entries.forEach { (title, content) -> + embed shouldContain title + embed shouldContain content + } + } + } + "should send welcome fallback if DMs are disabled" { + val dm = slot() + val sentMessages = mutableListOf() + TestUtil.prepareServerConfig(sentMessages) + Kagebot.welcomeUser( + mockk { + every { user } returns mockk { + every { id } returns 123 + every { sendMessage(capture(dm)) } + } + } + ) + } +}) + */ \ No newline at end of file diff --git a/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt b/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt index 19f2575..7075fca 100644 --- a/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt +++ b/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt @@ -50,7 +50,7 @@ object TestUtil { val server = mockk() every { server.icon.ifPresent(any()) } just Runs every { server.getTextChannelById(any()) } returns resultMock - Config.server = server + //Config.server = server } fun testMessageSuccess(content: String, result: String) {