From c1bb64d321e08de0617b6b9afd24046810148895 Mon Sep 17 00:00:00 2001 From: sschr15 Date: Mon, 18 Sep 2023 16:12:56 -0500 Subject: [PATCH] Fix + add features - Fix logging - Add mass-ping timeout exceptions - Add "ping groups": customizable role-less pingable groups - Fix strange weirdness in SettingsExtension.kt --- buildSrc/build.gradle.kts | 1 + .../src/main/kotlin/cozy-module.gradle.kts | 1 - libs.versions.toml | 8 +- src/main/kotlin/org/quiltmc/community/App.kt | 3 +- .../kotlin/org/quiltmc/community/_Utils.kt | 1 + .../collections/PingGroupCollection.kt | 38 ++ .../community/database/entities/PingGroup.kt | 24 + .../database/entities/ServerSettings.kt | 2 + .../database/migrations/AllMigrations.kt | 9 + .../quilt/extensions/UtilityExtension.kt | 268 +++++++++- .../moderation/ModerationExtension.kt | 45 ++ .../extensions/settings/SettingsExtension.kt | 464 +++++++++--------- src/main/resources/logback.groovy | 1 + src/main/resources/logbackCompiler.groovy | 462 +++++++++++++++++ 14 files changed, 1082 insertions(+), 245 deletions(-) create mode 100644 src/main/kotlin/org/quiltmc/community/database/collections/PingGroupCollection.kt create mode 100644 src/main/kotlin/org/quiltmc/community/database/entities/PingGroup.kt create mode 100644 src/main/resources/logbackCompiler.groovy diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index d0c93750..be5bd9ea 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -20,6 +20,7 @@ val versions = versionFileContents .filter { it.isNotBlank() } .map { it.trim() } .map { it.split("=") } + .filter { it.size == 2 } .associate { (k, v) -> k.trim() to v.substringAfter('"').substringBefore('"') } repositories { diff --git a/buildSrc/src/main/kotlin/cozy-module.gradle.kts b/buildSrc/src/main/kotlin/cozy-module.gradle.kts index e4b73360..11e4336e 100644 --- a/buildSrc/src/main/kotlin/cozy-module.gradle.kts +++ b/buildSrc/src/main/kotlin/cozy-module.gradle.kts @@ -80,7 +80,6 @@ tasks { withType().configureEach { kotlinOptions { jvmTarget = "17" - freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" } } diff --git a/libs.versions.toml b/libs.versions.toml index 5c51edd3..e24273ba 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -1,6 +1,6 @@ [versions] detekt = "1.23.1" -kotlin = "1.9.0" +kotlin = "1.9.10" graphql = "6.5.3" autolink = "0.11.0" @@ -20,11 +20,11 @@ kaml = "0.55.0" kmongo = "4.10.0" kordex = "1.5.9-SNAPSHOT" kotlintest = "3.4.2" -ksp = "1.9.0-1.0.13" +ksp = "1.9.10-1.0.13" ktor = "2.3.3" kx-ser = "1.6.0" -logback = "1.4.5" -logback-groovy = "1.14.0" +logback = "1.4.11" +logback-groovy = "1.14.5" logging = "3.0.5" moshi = "1.15.0" rgxgen = "1.4" diff --git a/src/main/kotlin/org/quiltmc/community/App.kt b/src/main/kotlin/org/quiltmc/community/App.kt index eb47a8ca..9caa9ad2 100644 --- a/src/main/kotlin/org/quiltmc/community/App.kt +++ b/src/main/kotlin/org/quiltmc/community/App.kt @@ -25,6 +25,7 @@ import com.kotlindiscord.kord.extensions.utils.getKoin import dev.kord.core.entity.Guild import dev.kord.core.event.interaction.ChatInputCommandInteractionCreateEvent import dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEvent +import dev.kord.gateway.ALL import dev.kord.gateway.Intents import dev.kord.gateway.PrivilegedIntent import dev.kord.rest.builder.message.create.embed @@ -66,7 +67,7 @@ suspend fun setupLadysnake() = ExtensibleBot(DISCORD_TOKEN) { } intents { - +Intents.all + +Intents.ALL } members { diff --git a/src/main/kotlin/org/quiltmc/community/_Utils.kt b/src/main/kotlin/org/quiltmc/community/_Utils.kt index ff6329f5..b5584ecd 100644 --- a/src/main/kotlin/org/quiltmc/community/_Utils.kt +++ b/src/main/kotlin/org/quiltmc/community/_Utils.kt @@ -169,6 +169,7 @@ suspend fun ExtensibleBotBuilder.database(migrate: Boolean = false) { single { TagsCollection() } bind TagsCollection::class single { WelcomeChannelCollection() } bind WelcomeChannelCollection::class single { QuoteCollection() } bind QuoteCollection::class + single { PingGroupCollection() } bind PingGroupCollection::class } if (migrate) { diff --git a/src/main/kotlin/org/quiltmc/community/database/collections/PingGroupCollection.kt b/src/main/kotlin/org/quiltmc/community/database/collections/PingGroupCollection.kt new file mode 100644 index 00000000..9489b4b2 --- /dev/null +++ b/src/main/kotlin/org/quiltmc/community/database/collections/PingGroupCollection.kt @@ -0,0 +1,38 @@ +package org.quiltmc.community.database.collections + +import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent +import dev.kord.common.entity.Snowflake +import org.koin.core.component.inject +import org.litote.kmongo.and +import org.litote.kmongo.contains +import org.litote.kmongo.eq +import org.quiltmc.community.database.Collection +import org.quiltmc.community.database.Database +import org.quiltmc.community.database.entities.PingGroup + +class PingGroupCollection : KordExKoinComponent { + private val database: Database by inject() + private val col = database.mongo.getCollection(name) + + suspend fun get(id: String) = + col.findOne(PingGroup::_id eq id) + + suspend fun set(pingGroup: PingGroup) = + col.save(pingGroup) + + suspend fun getAll(guild: Snowflake, allowAny: Boolean): List { + val guildEquals = PingGroup::guildId eq guild + if (allowAny) return col.find(guildEquals).toList() + return col.find(and(guildEquals, PingGroup::canSelfSubscribe eq true)).toList() + } + + suspend fun getByUser(guild: Snowflake, user: Snowflake): List { + val guildEquals = PingGroup::guildId eq guild + return col.find(and(guildEquals, PingGroup::users contains user)).toList() + } + + suspend fun delete(id: String) = + col.deleteOne(PingGroup::_id eq id) + + companion object : Collection("ping-groups") +} diff --git a/src/main/kotlin/org/quiltmc/community/database/entities/PingGroup.kt b/src/main/kotlin/org/quiltmc/community/database/entities/PingGroup.kt new file mode 100644 index 00000000..18c9eefb --- /dev/null +++ b/src/main/kotlin/org/quiltmc/community/database/entities/PingGroup.kt @@ -0,0 +1,24 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +@file:Suppress("DataClassShouldBeImmutable") // Well, yes, but actually no. + +package org.quiltmc.community.database.entities + +import dev.kord.common.entity.Snowflake +import kotlinx.serialization.Serializable +import org.quiltmc.community.database.Entity + +@Serializable +data class PingGroup( + override val _id: String, + val name: String, + val guildId: Snowflake, + var canSelfSubscribe: Boolean = false, + val emoji: String? = null, + val desc: String? = null, + val users: MutableSet = mutableSetOf(), +) : Entity diff --git a/src/main/kotlin/org/quiltmc/community/database/entities/ServerSettings.kt b/src/main/kotlin/org/quiltmc/community/database/entities/ServerSettings.kt index c333ff6c..761bb7ca 100644 --- a/src/main/kotlin/org/quiltmc/community/database/entities/ServerSettings.kt +++ b/src/main/kotlin/org/quiltmc/community/database/entities/ServerSettings.kt @@ -44,6 +44,8 @@ data class ServerSettings( var leaveServer: Boolean = false, val threadOnlyChannels: MutableSet = mutableSetOf(), var defaultThreadLength: ArchiveDuration? = null, + + val pingTimeoutBlacklist: MutableSet = mutableSetOf(), ) : Entity { suspend fun save() { val collection = getKoin().get() diff --git a/src/main/kotlin/org/quiltmc/community/database/migrations/AllMigrations.kt b/src/main/kotlin/org/quiltmc/community/database/migrations/AllMigrations.kt index 223851b8..9c2f611d 100644 --- a/src/main/kotlin/org/quiltmc/community/database/migrations/AllMigrations.kt +++ b/src/main/kotlin/org/quiltmc/community/database/migrations/AllMigrations.kt @@ -312,4 +312,13 @@ object AllMigrations { } } } + + suspend fun v26(db: CoroutineDatabase) { + db.getCollection(ServerSettingsCollection.name).updateMany( + ServerSettings::pingTimeoutBlacklist exists false, + setValue(ServerSettings::pingTimeoutBlacklist, mutableSetOf()) + ) + + db.createCollection(PingGroupCollection.name) + } } diff --git a/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/UtilityExtension.kt b/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/UtilityExtension.kt index 75950fa8..678072a4 100644 --- a/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/UtilityExtension.kt +++ b/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/UtilityExtension.kt @@ -16,6 +16,7 @@ import com.kotlindiscord.kord.extensions.DISCORD_RED import com.kotlindiscord.kord.extensions.DiscordRelayedException import com.kotlindiscord.kord.extensions.annotations.DoNotChain import com.kotlindiscord.kord.extensions.checks.* +import com.kotlindiscord.kord.extensions.checks.types.CheckContext import com.kotlindiscord.kord.extensions.commands.Arguments import com.kotlindiscord.kord.extensions.commands.application.slash.ephemeralSubCommand import com.kotlindiscord.kord.extensions.commands.application.slash.publicSubCommand @@ -23,6 +24,8 @@ import com.kotlindiscord.kord.extensions.commands.converters.impl.* import com.kotlindiscord.kord.extensions.components.ComponentContainer import com.kotlindiscord.kord.extensions.components.components import com.kotlindiscord.kord.extensions.components.ephemeralButton +import com.kotlindiscord.kord.extensions.components.ephemeralStringSelectMenu +import com.kotlindiscord.kord.extensions.components.forms.ModalForm import com.kotlindiscord.kord.extensions.extensions.* import com.kotlindiscord.kord.extensions.i18n.SupportedLocales import com.kotlindiscord.kord.extensions.time.TimestampType @@ -39,6 +42,7 @@ import dev.kord.core.behavior.channel.threads.edit import dev.kord.core.behavior.createRole import dev.kord.core.behavior.edit import dev.kord.core.entity.channel.GuildMessageChannel +import dev.kord.core.entity.channel.MessageChannel import dev.kord.core.entity.channel.TextChannel import dev.kord.core.entity.channel.thread.ThreadChannel import dev.kord.core.event.channel.thread.TextChannelThreadCreateEvent @@ -61,11 +65,9 @@ import kotlinx.serialization.json.Json import org.koin.core.component.inject import org.quiltmc.community.* import org.quiltmc.community.cozy.modules.moderation.compareTo -import org.quiltmc.community.database.collections.GlobalSettingsCollection -import org.quiltmc.community.database.collections.OwnedThreadCollection -import org.quiltmc.community.database.collections.SuggestionsCollection -import org.quiltmc.community.database.collections.UserFlagsCollection +import org.quiltmc.community.database.collections.* import org.quiltmc.community.database.entities.OwnedThread +import org.quiltmc.community.database.entities.PingGroup import org.quiltmc.community.database.entities.UserFlags import org.quiltmc.community.database.getSettings import org.quiltmc.community.modes.quilt.extensions.suggestions.SuggestionStatus @@ -108,8 +110,9 @@ class UtilityExtension : Extension() { MANAGER_ROLES, MODERATOR_ROLES ).flatten() - private val threads: OwnedThreadCollection by inject() + private val pingGroups: PingGroupCollection by inject() + private val threads: OwnedThreadCollection by inject() private val userFlags: UserFlagsCollection by inject() private val suggestionsExtension: SuggestionsExtension? = bot.findExtension() @@ -1434,6 +1437,184 @@ class UtilityExtension : Extension() { } } } + + ephemeralUserCommand { + name = "Manage Ping Groups" + + allowInDms = false + + guild(guildId) + + check { + any( + { hasBaseModeratorRole() }, + { failIf(event.interaction.user.id != event.interaction.targetId) } + ) + } + + action { + val checks = CheckContext(event, getLocale()) + checks.hasBaseModeratorRole() + val allowAny = checks.passed + + val userId = event.interaction.targetId + + val selectable = pingGroups.getAll(guildId, allowAny).toSet() + val removable = pingGroups.getByUser(guildId, userId).toSet() + + val groups = selectable + removable + + if (groups.isEmpty()) { + respond { + content = "**Error:** There are no ping groups that you can add or remove." + } + return@action + } + + respond { + content = "Please select the ping groups you'd like to be subscribed to. " + + "If you'd like to unsubscribe from a group, simply unselect it." + + components { + ephemeralStringSelectMenu { + placeholder = "Ping Groups" + + minimumChoices = 0 + maximumChoices = null // no limit + + for (group in groups.sortedBy { it.name }) { + option(group.name, group._id) { + group.emoji?.takeUnless { it.isBlank() }?.toReaction()?.let(::emoji) + description = group.desc + default = group in removable + } + } + + action { + val values = event.interaction.values.toSet() + val selected = selectable.filter { it._id in values } + val removed = removable.filter { it._id !in values } + + for (group in selected) { + group.users.add(userId) + pingGroups.set(group) + } + + for (group in removed) { + group.users.remove(userId) + pingGroups.set(group) + } + + respond { + content = "Ping groups updated." + } + } + } + } + } + } + } + + ephemeralSlashCommand { + name = "ping-groups" + description = "Manage ping groups" + + allowInDms = false + + guild(guildId) + + check { hasBaseModeratorRole() } + + ephemeralSubCommand(::PingGroupCreateModal) { + name = "create" + description = "Create a new ping group" + + guild(guildId) + + check { hasBaseModeratorRole() } + + action { + it!! + + val newGroup = PingGroup( + guildId = guildId, + _id = it.groupId.value!!, + name = it.name.value!!, + canSelfSubscribe = it.allowAnyone.value.toBoolean(), + desc = it.desc.value, + emoji = it.emoji.value + ) + + pingGroups.set(newGroup) + + respond { + content = "Ping group created." + } + } + } + + ephemeralSubCommand(::PingGroupDeleteArguments) { + name = "delete" + description = "Delete a ping group" + + guild(guildId) + + check { hasBaseModeratorRole() } + + action { + val result = pingGroups.delete(arguments.id) + + if (result.deletedCount != 0L) { + respond { + content = "Ping group deleted." + } + } else { + respond { + content = "**Error:** Ping group not found." + } + } + } + } + + ephemeralSubCommand(::PingGroupPingArguments) { + // the real meat and potatoes + name = "ping" + description = "Ping a ping group" + + guild(guildId) + + check { hasBaseModeratorRole() } + + action { + val group = pingGroups.get(arguments.id) ?: run { + respond { + content = "**Error:** Ping group not found." + } + return@action + } + + val message = arguments.message ?: "Ping to group '${group.name}' from ${user.mention}" + val channel = arguments.channel?.asChannelOfOrNull() ?: channel.asChannel() + + val fullMessage = buildString { + append(message) + append(" (") + for (user in group.users) { + if (length > 0) append(", ") + append("<@") + append(user) + append(">") + } + append(")") + } + channel.createMessage(fullMessage) + + respond { + content = "Ping sent." + } + } + } + } } event { @@ -1819,4 +2000,81 @@ class UtilityExtension : Extension() { description = "Guild to use. To specify this guild, use its ID." } } + + inner class PingGroupPingArguments : Arguments() { + val id by string { + name = "id" + description = "ID of the ping group to ping" + + autoComplete { event -> + val groups = pingGroups.getAll(event.interaction.getChannel().asChannelOf().guildId, true) + suggestStringMap(groups.associate { it.name to it._id }) + } + } + + val message by optionalString { + name = "message" + description = "Message to send to the ping group" + + mutate { + it?.trim() + } + } + + val channel by optionalChannel { + name = "channel" + description = "Channel to send the message in, if not the current one" + } + } + + inner class PingGroupDeleteArguments : Arguments() { + val id by string { + name = "id" + description = "ID of the ping group to delete" + + autoComplete { event -> + val groups = pingGroups.getAll(event.interaction.getChannel().asChannelOf().guildId, true) + suggestStringMap(groups.associate { it.name to it._id }) + } + } + } + + inner class PingGroupCreateModal : ModalForm() { + override var title = "Create Ping Group" + + val groupId = lineText { + label = "ID used to refer to the ping group" + placeholder = "cool-group" + maxLength = 100 + required = true + } + + val name = lineText { + label = "Name of the ping group" + placeholder = "Cool Group" + maxLength = 100 + required = true + } + + val desc = lineText { + label = "Description of the ping group" + placeholder = "A cool group for cool people" + maxLength = 100 + required = false + } + + val emoji = lineText { + label = "Emoji to use for the ping group" + placeholder = "👍" + maxLength = 100 + required = false + } + + val allowAnyone = lineText { + label = "Allow anyone to subscribe? (true/false)" + placeholder = "false by default" + maxLength = 5 + required = false + } + } } diff --git a/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/moderation/ModerationExtension.kt b/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/moderation/ModerationExtension.kt index 97b06ffa..ce8b39de 100644 --- a/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/moderation/ModerationExtension.kt +++ b/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/moderation/ModerationExtension.kt @@ -62,6 +62,7 @@ import org.quiltmc.community.database.entities.InvalidMention import org.quiltmc.community.database.entities.InvalidMention.Type.ROLE import org.quiltmc.community.database.entities.InvalidMention.Type.USER import org.quiltmc.community.database.entities.UserRestrictions +import org.quiltmc.community.database.getSettings import org.quiltmc.community.modes.quilt.extensions.rotatinglog.MessageLogExtension import kotlin.time.Duration.Companion.ZERO import kotlin.time.Duration.Companion.days @@ -218,6 +219,14 @@ class ModerationExtension( check { failIf(event.message.author?.isBot == true) } check { failIf(event.guildId == null) } check { notHasBaseModeratorRole() } + check { + failIf( + event.getGuildOrNull() + ?.getSettings() + ?.pingTimeoutBlacklist + ?.contains(event.message.data.authorId) == true + ) + } action { val guild = event.guildId!! @@ -387,6 +396,42 @@ class ModerationExtension( } } } + + ephemeralSubCommand({ RequiredUser("The user to blacklist") }) { + check { hasBaseModeratorRole() } + + name = "allow-massping" + description = "Blacklist a user from ping timeouts, allowing them to ping as many people as they want." + + action { + val settings = guild?.getSettings() ?: return@action + + settings.pingTimeoutBlacklist.add(arguments.user.id) + settings.save() + + respond { + content = "User ${arguments.user.mention} has been blacklisted from ping timeouts." + } + } + } + + ephemeralSubCommand({ RequiredUser("The user to remove from the blacklist") }) { + check { hasBaseModeratorRole() } + + name = "disallow-massping" + description = "Remove a user from the ping timeout blacklist." + + action { + val settings = guild?.getSettings() ?: return@action + + settings.pingTimeoutBlacklist.remove(arguments.user.id) + settings.save() + + respond { + content = "User ${arguments.user.mention} has been removed from the ping timeout blacklist." + } + } + } } } if (Module.USER_MANAGEMENT in enabledModules) { diff --git a/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/settings/SettingsExtension.kt b/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/settings/SettingsExtension.kt index 43a4f80a..485e09ec 100644 --- a/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/settings/SettingsExtension.kt +++ b/src/main/kotlin/org/quiltmc/community/modes/quilt/extensions/settings/SettingsExtension.kt @@ -529,314 +529,310 @@ class SettingsExtension : Extension() { } } - ephemeralSubCommand(::TopMessageChannelGuildArg) { - name = "cozy-log-channel" - description = "Configure the channel Cozy should send log messages to" - } - ephemeralSubCommand(::RoleServerArg) { - name = "add-moderator-role" - description = "Add a role that should be given moderator permissions" + ephemeralSubCommand(::RoleServerArg) { + name = "add-moderator-role" + description = "Add a role that should be given moderator permissions" - action { - val context = CheckContext(event, getLocale()) + action { + val context = CheckContext(event, getLocale()) - if (arguments.serverId != null) { - context.hasPermissionInMainGuild(Permission.Administrator) + if (arguments.serverId != null) { + context.hasPermissionInMainGuild(Permission.Administrator) - if (!context.passed) { + if (!context.passed) { + respond { + content = ":x: Only Quilt community managers can modify settings for other servers." + } + + return@action + } + } + + val settings = if (arguments.serverId == null) { + serverSettings.get(guild!!.id) + } else { + serverSettings.get(arguments.serverId!!) + } + + if (settings == null) { respond { - content = ":x: Only Quilt community managers can modify settings for other servers." + content = ":x: Unknown guild ID: `${arguments.serverId}`" } return@action } - } - val settings = if (arguments.serverId == null) { - serverSettings.get(guild!!.id) - } else { - serverSettings.get(arguments.serverId!!) - } + if (arguments.role.guildId != settings._id) { + respond { + content = ":x: That role doesn't belong to the guild with ID: `${settings._id}`" + } - if (settings == null) { - respond { - content = ":x: Unknown guild ID: `${arguments.serverId}`" + return@action } - return@action - } + if (arguments.role.id in settings.moderatorRoles) { + respond { + content = ":x: That role is already marked as a moderator role" + } - if (arguments.role.guildId != settings._id) { - respond { - content = ":x: That role doesn't belong to the guild with ID: `${settings._id}`" + return@action } - return@action - } + settings.moderatorRoles.add(arguments.role.id) + settings.save() - if (arguments.role.id in settings.moderatorRoles) { respond { - content = ":x: That role is already marked as a moderator role" + content = "Moderator role added: ${arguments.role.mention}" } - - return@action } + } - settings.moderatorRoles.add(arguments.role.id) - settings.save() + ephemeralSubCommand(::RoleServerArg) { + name = "remove-moderator-role" + description = "Remove a configured moderator role" - respond { - content = "Moderator role added: ${arguments.role.mention}" - } - } - } + action { + val context = CheckContext(event, getLocale()) - ephemeralSubCommand(::RoleServerArg) { - name = "remove-moderator-role" - description = "Remove a configured moderator role" + if (arguments.serverId != null) { + context.hasPermissionInMainGuild(Permission.Administrator) - action { - val context = CheckContext(event, getLocale()) + if (!context.passed) { + respond { + content = ":x: Only Quilt community managers can modify settings for other servers." + } - if (arguments.serverId != null) { - context.hasPermissionInMainGuild(Permission.Administrator) + return@action + } + } + + val settings = if (arguments.serverId == null) { + serverSettings.get(guild!!.id) + } else { + serverSettings.get(arguments.serverId!!) + } - if (!context.passed) { + if (settings == null) { respond { - content = ":x: Only Quilt community managers can modify settings for other servers." + content = ":x: Unknown guild ID: `${arguments.serverId}`" } return@action } - } - val settings = if (arguments.serverId == null) { - serverSettings.get(guild!!.id) - } else { - serverSettings.get(arguments.serverId!!) - } + if (arguments.role.guildId != settings._id) { + respond { + content = ":x: That role doesn't belong to the guild with ID: `${settings._id}`" + } - if (settings == null) { - respond { - content = ":x: Unknown guild ID: `${arguments.serverId}`" + return@action } - return@action - } + if (arguments.role.id !in settings.moderatorRoles) { + respond { + content = ":x: That role is not marked as a moderator role" + } - if (arguments.role.guildId != settings._id) { - respond { - content = ":x: That role doesn't belong to the guild with ID: `${settings._id}`" + return@action } - return@action - } + settings.moderatorRoles.remove(arguments.role.id) + settings.save() - if (arguments.role.id !in settings.moderatorRoles) { respond { - content = ":x: That role is not marked as a moderator role" + content = "Moderator role removed: ${arguments.role.mention}" } - - return@action } + } - settings.moderatorRoles.remove(arguments.role.id) - settings.save() + ephemeralSubCommand(::TopMessageChannelGuildArg) { + name = "application-log-channel" + description = "Configure the channel Cozy should send server applications messages to" - respond { - content = "Moderator role removed: ${arguments.role.mention}" - } - } - } + action { + val context = CheckContext(event, getLocale()) - ephemeralSubCommand(::TopMessageChannelGuildArg) { - name = "application-log-channel" - description = "Configure the channel Cozy should send server applications messages to" + if (arguments.serverId != null) { + context.hasPermissionInMainGuild(Permission.Administrator) - action { - val context = CheckContext(event, getLocale()) + if (!context.passed) { + respond { + content = ":x: Only Quilt community managers can modify settings for other servers." + } - if (arguments.serverId != null) { - context.hasPermissionInMainGuild(Permission.Administrator) + return@action + } + } + + val settings = if (arguments.serverId == null) { + serverSettings.get(guild!!.id) + } else { + serverSettings.get(arguments.serverId!!) + } - if (!context.passed) { + if (settings == null) { respond { - content = ":x: Only Quilt community managers can modify settings for other servers." + content = ":x: Unknown guild ID: `${arguments.serverId}`" } return@action } - } - val settings = if (arguments.serverId == null) { - serverSettings.get(guild!!.id) - } else { - serverSettings.get(arguments.serverId!!) - } + if (arguments.channel == null) { + respond { + content = "**Current application logging channel:** " + + "<#${settings.applicationLogChannel}>" + } - if (settings == null) { - respond { - content = ":x: Unknown guild ID: `${arguments.serverId}`" + return@action } - return@action - } + val channel = event.kord.getChannelOf(arguments.channel!!.id)!! - if (arguments.channel == null) { - respond { - content = "**Current application logging channel:** " + - "<#${settings.applicationLogChannel}>" - } + if (channel.guildId != settings._id) { + respond { + content = ":x: That channel doesn't belong to the guild with ID: `${settings._id}`" + } - return@action - } + return@action + } - val channel = event.kord.getChannelOf(arguments.channel!!.id)!! + settings.applicationLogChannel = channel.id + settings.save() - if (channel.guildId != settings._id) { respond { - content = ":x: That channel doesn't belong to the guild with ID: `${settings._id}`" + content = "**Application logging channel set:** ${channel.mention}" } - - return@action } + } - settings.applicationLogChannel = channel.id - settings.save() + ephemeralSubCommand(::TopMessageChannelGuildArg) { + name = "application-threads-channel" + description = "Configure the channel Cozy should create server application threads within" - respond { - content = "**Application logging channel set:** ${channel.mention}" - } - } - } + action { + val context = CheckContext(event, getLocale()) + + if (arguments.serverId != null) { + context.hasPermissionInMainGuild(Permission.Administrator) - ephemeralSubCommand(::TopMessageChannelGuildArg) { - name = "application-threads-channel" - description = "Configure the channel Cozy should create server application threads within" + if (!context.passed) { + respond { + content = ":x: Only Quilt community managers can modify settings for other servers." + } - action { - val context = CheckContext(event, getLocale()) + return@action + } + } - if (arguments.serverId != null) { - context.hasPermissionInMainGuild(Permission.Administrator) + val settings = if (arguments.serverId == null) { + serverSettings.get(guild!!.id) + } else { + serverSettings.get(arguments.serverId!!) + } - if (!context.passed) { + if (settings == null) { respond { - content = ":x: Only Quilt community managers can modify settings for other servers." + content = ":x: Unknown guild ID: `${arguments.serverId}`" } return@action } - } - val settings = if (arguments.serverId == null) { - serverSettings.get(guild!!.id) - } else { - serverSettings.get(arguments.serverId!!) - } + if (arguments.channel == null) { + respond { + content = "**Current application threads channel:** " + + "<#${settings.applicationThreadsChannel}>" + } - if (settings == null) { - respond { - content = ":x: Unknown guild ID: `${arguments.serverId}`" + return@action } - return@action - } + val channel = event.kord.getChannelOf(arguments.channel!!.id)!! - if (arguments.channel == null) { - respond { - content = "**Current application threads channel:** " + - "<#${settings.applicationThreadsChannel}>" - } + if (channel.guildId != settings._id) { + respond { + content = ":x: That channel doesn't belong to the guild with ID: `${settings._id}`" + } - return@action - } + return@action + } - val channel = event.kord.getChannelOf(arguments.channel!!.id)!! + settings.applicationThreadsChannel = channel.id + settings.save() - if (channel.guildId != settings._id) { respond { - content = ":x: That channel doesn't belong to the guild with ID: `${settings._id}`" + content = "**Application threads channel set:** ${channel.mention}" } - - return@action - } - - settings.applicationThreadsChannel = channel.id - settings.save() - - respond { - content = "**Application threads channel set:** ${channel.mention}" } } - } - ephemeralSubCommand(::TopMessageChannelGuildArg) { - name = "cozy-log-channel" - description = "Configure the channel Cozy should send log messages to" + ephemeralSubCommand(::TopMessageChannelGuildArg) { + name = "cozy-log-channel" + description = "Configure the channel Cozy should send log messages to" - action { - val context = CheckContext(event, getLocale()) + action { + val context = CheckContext(event, getLocale()) - if (arguments.serverId != null) { - with(context) { - any( - { hasPermissionInMainGuild(Permission.Administrator) }, - { failIfNot { event.interaction.user.id in OVERRIDING_USERS } } - ) - } + if (arguments.serverId != null) { + with(context) { + any( + { hasPermissionInMainGuild(Permission.Administrator) }, + { failIfNot { event.interaction.user.id in OVERRIDING_USERS } } + ) + } - if (!context.passed) { - respond { - content = - ":x: Only Ladysnake community managers can modify settings for other servers." - } + if (!context.passed) { + respond { + content = + ":x: Only Ladysnake community managers can modify settings for other servers." + } - return@action - } - } + return@action + } + } - val settings = if (arguments.serverId == null) { - serverSettings.get(guild!!.id) - } else { - serverSettings.get(arguments.serverId!!) - } + val settings = if (arguments.serverId == null) { + serverSettings.get(guild!!.id) + } else { + serverSettings.get(arguments.serverId!!) + } - if (settings == null) { - respond { - content = ":x: Unknown guild ID: `${arguments.serverId}`" - } + if (settings == null) { + respond { + content = ":x: Unknown guild ID: `${arguments.serverId}`" + } - return@action - } + return@action + } - if (arguments.channel == null) { - respond { - content = "**Current Cozy logging channel:** <#${settings.cozyLogChannel}>" - } + if (arguments.channel == null) { + respond { + content = "**Current Cozy logging channel:** <#${settings.cozyLogChannel}>" + } - return@action - } + return@action + } - val channel = event.kord.getChannelOf(arguments.channel!!.id)!! + val channel = event.kord.getChannelOf(arguments.channel!!.id)!! - if (channel.guildId != settings._id) { - respond { - content = - ":x: That channel doesn't belong to the guild with ID: `${settings._id}`" - } + if (channel.guildId != settings._id) { + respond { + content = + ":x: That channel doesn't belong to the guild with ID: `${settings._id}`" + } - return@action - } + return@action + } - settings.cozyLogChannel = channel.id - settings.save() + settings.cozyLogChannel = channel.id + settings.save() - respond { - content = "**Cozy logging channel set:** ${channel.mention}" - } - } - } + respond { + content = "**Cozy logging channel set:** ${channel.mention}" + } + } + } ephemeralSubCommand(::TopMessageChannelGuildArg) { name = "filter-log-channel" @@ -1098,39 +1094,39 @@ class SettingsExtension : Extension() { } } - ephemeralSubCommand(::SingleRoleArg) { - name = "verification-role" - description = "For Quilt servers: Set (or clear) the verification role" + ephemeralSubCommand(::SingleRoleArg) { + name = "verification-role" + description = "For Quilt servers: Set (or clear) the verification role" - check { hasPermissionInMainGuild(Permission.Administrator) } + check { hasPermissionInMainGuild(Permission.Administrator) } - action { - val settings = if (arguments.serverId == null) { - serverSettings.get(guild!!.id) - } else { - serverSettings.get(arguments.serverId!!) - } - - if (settings == null) { - respond { - content = ":x: Unknown guild ID: `${arguments.serverId}`" + action { + val settings = if (arguments.serverId == null) { + serverSettings.get(guild!!.id) + } else { + serverSettings.get(arguments.serverId!!) } - return@action - } + if (settings == null) { + respond { + content = ":x: Unknown guild ID: `${arguments.serverId}`" + } - settings.verificationRole = arguments.role?.id - settings.save() + return@action + } - respond { - content = if (settings.verificationRole == null) { - "**Verification role unset**" - } else { - "**Verification role set:** <@&${settings.verificationRole}>" - } - } - } - } + settings.verificationRole = arguments.role?.id + settings.save() + + respond { + content = if (settings.verificationRole == null) { + "**Verification role unset**" + } else { + "**Verification role set:** <@&${settings.verificationRole}>" + } + } + } + } ephemeralSubCommand(::ShouldLeaveArg) { name = "set-leave-server" diff --git a/src/main/resources/logback.groovy b/src/main/resources/logback.groovy index 607e2ad7..68c0fd28 100644 --- a/src/main/resources/logback.groovy +++ b/src/main/resources/logback.groovy @@ -4,6 +4,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import ch.qos.logback.core.ConsoleAppender import ch.qos.logback.core.joran.spi.ConsoleTarget import org.quiltmc.community.DiscordLogAppender diff --git a/src/main/resources/logbackCompiler.groovy b/src/main/resources/logbackCompiler.groovy new file mode 100644 index 00000000..44f5a931 --- /dev/null +++ b/src/main/resources/logbackCompiler.groovy @@ -0,0 +1,462 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +importsAcceptList = [ + 'org.quiltmc.community.DiscordLogAppender', + + 'ch.qos.logback.core.testUtil.SampleConverter', + + 'ch.qos.logback.core.testUtil.StringListAppender', + 'java.lang.Object', + 'org.springframework.beans.factory.annotation.Autowired', + 'java.nio.charset.Charset.forName', + 'com.logentries.logback.LogentriesAppender', + 'grails.util.BuildSettings', + 'grails.util.Environment', + 'io.micronaut.context.env.Environment', + 'org.slf4j.MDC', + 'org.springframework.boot.logging.logback.ColorConverter', + 'org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter', + 'java.nio.charset.Charset', + 'java.nio.charset.StandardCharsets', + + 'ch.qos.logback.core.BasicStatusManager', + 'ch.qos.logback.core.ConsoleAppender', + 'ch.qos.logback.core.hook.ShutdownHook', + 'ch.qos.logback.core.hook.ShutdownHookBase', + 'ch.qos.logback.core.hook.DelayingShutdownHook', + 'ch.qos.logback.core.spi.PropertyContainer', + 'ch.qos.logback.core.spi.ContextAwareBase', + 'ch.qos.logback.core.spi.LogbackLock', + 'ch.qos.logback.core.spi.FilterAttachableImpl', + 'ch.qos.logback.core.spi.ContextAwareImpl', + 'ch.qos.logback.core.spi.ScanException', + 'ch.qos.logback.core.spi.DeferredProcessingAware', + 'ch.qos.logback.core.spi.ContextAware', + 'ch.qos.logback.core.spi.LifeCycle', + 'ch.qos.logback.core.spi.FilterReply', + 'ch.qos.logback.core.spi.PreSerializationTransformer', + 'ch.qos.logback.core.spi.AppenderAttachable', + 'ch.qos.logback.core.spi.CyclicBufferTracker', + 'ch.qos.logback.core.spi.FilterAttachable', + 'ch.qos.logback.core.spi.ComponentTracker', + 'ch.qos.logback.core.spi.AppenderAttachableImpl', + 'ch.qos.logback.core.spi.PropertyDefiner', + 'ch.qos.logback.core.spi.AbstractComponentTracker', + 'ch.qos.logback.core.property.FileExistsPropertyDefiner', + 'ch.qos.logback.core.property.ResourceExistsPropertyDefiner', + 'ch.qos.logback.core.CoreConstants', + 'ch.qos.logback.core.layout.EchoLayout', + 'ch.qos.logback.core.Appender', + 'ch.qos.logback.core.joran.JoranConfiguratorBase', + 'ch.qos.logback.core.joran.spi.ActionException', + 'ch.qos.logback.core.joran.spi.HostClassAndPropertyDouble', + 'ch.qos.logback.core.joran.spi.JoranException', + 'ch.qos.logback.core.joran.spi.NoAutoStart', + 'ch.qos.logback.core.joran.spi.EventPlayer', + 'ch.qos.logback.core.joran.spi.XMLUtil', + 'ch.qos.logback.core.joran.spi.ConsoleTarget', + 'ch.qos.logback.core.joran.spi.Interpreter', + 'ch.qos.logback.core.joran.spi.SimpleRuleStore', + 'ch.qos.logback.core.joran.spi.InterpretationContext', + 'ch.qos.logback.core.joran.spi.RuleStore', + 'ch.qos.logback.core.joran.spi.NoAutoStartUtil', + 'ch.qos.logback.core.joran.spi.ElementSelector', + 'ch.qos.logback.core.joran.spi.ConfigurationWatchList', + 'ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry', + 'ch.qos.logback.core.joran.spi.DefaultClass', + 'ch.qos.logback.core.joran.spi.ElementPath', + 'ch.qos.logback.core.joran.conditional.ThenOrElseActionBase', + 'ch.qos.logback.core.joran.conditional.Condition', + 'ch.qos.logback.core.joran.conditional.PropertyWrapperForScripts', + 'ch.qos.logback.core.joran.conditional.ThenAction', + 'ch.qos.logback.core.joran.conditional.PropertyEvalScriptBuilder', + 'ch.qos.logback.core.joran.conditional.ElseAction', + 'ch.qos.logback.core.joran.conditional.IfAction', + 'ch.qos.logback.core.joran.util.beans.BeanDescriptionCache', + 'ch.qos.logback.core.joran.util.beans.BeanDescriptionFactory', + 'ch.qos.logback.core.joran.util.beans.BeanDescription', + 'ch.qos.logback.core.joran.util.beans.BeanUtil', + 'ch.qos.logback.core.joran.util.PropertySetter', + 'ch.qos.logback.core.joran.util.ConfigurationWatchListUtil', + 'ch.qos.logback.core.joran.util.StringToObjectConverter', + 'ch.qos.logback.core.joran.GenericConfigurator', + 'ch.qos.logback.core.joran.action.ImplicitAction', + 'ch.qos.logback.core.joran.action.IncludeAction', + 'ch.qos.logback.core.joran.action.NOPAction', + 'ch.qos.logback.core.joran.action.IADataForBasicProperty', + 'ch.qos.logback.core.joran.action.TimestampAction', + 'ch.qos.logback.core.joran.action.AbstractEventEvaluatorAction', + 'ch.qos.logback.core.joran.action.ParamAction', + 'ch.qos.logback.core.joran.action.AppenderAction', + 'ch.qos.logback.core.joran.action.DefinePropertyAction', + 'ch.qos.logback.core.joran.action.StatusListenerAction', + 'ch.qos.logback.core.joran.action.ContextPropertyAction', + 'ch.qos.logback.core.joran.action.NestedComplexPropertyIA', + 'ch.qos.logback.core.joran.action.NestedBasicPropertyIA', + 'ch.qos.logback.core.joran.action.Action', + 'ch.qos.logback.core.joran.action.AppenderRefAction', + 'ch.qos.logback.core.joran.action.ActionUtil', + 'ch.qos.logback.core.joran.action.ShutdownHookAction', + 'ch.qos.logback.core.joran.action.IADataForComplexProperty', + 'ch.qos.logback.core.joran.action.ConversionRuleAction', + 'ch.qos.logback.core.joran.action.ActionConst', + 'ch.qos.logback.core.joran.action.PropertyAction', + 'ch.qos.logback.core.joran.action.NewRuleAction', + 'ch.qos.logback.core.joran.node.ComponentNode', + 'ch.qos.logback.core.joran.event.EndEvent', + 'ch.qos.logback.core.joran.event.SaxEventRecorder', + 'ch.qos.logback.core.joran.event.SaxEvent', + 'ch.qos.logback.core.joran.event.BodyEvent', + 'ch.qos.logback.core.joran.event.StartEvent', + 'ch.qos.logback.core.joran.event.InPlayListener', + 'ch.qos.logback.core.joran.event.stax.EndEvent', + 'ch.qos.logback.core.joran.event.stax.StaxEventRecorder', + 'ch.qos.logback.core.joran.event.stax.BodyEvent', + 'ch.qos.logback.core.joran.event.stax.StartEvent', + 'ch.qos.logback.core.joran.event.stax.StaxEvent', + 'ch.qos.logback.core.LogbackException', + 'ch.qos.logback.core.PropertyDefinerBase', + 'ch.qos.logback.core.helpers.CyclicBuffer', + 'ch.qos.logback.core.helpers.ThrowableToStringArray', + 'ch.qos.logback.core.helpers.Transform', + 'ch.qos.logback.core.helpers.NOPAppender', + 'ch.qos.logback.core.net.LoginAuthenticator', + 'ch.qos.logback.core.net.DefaultSocketConnector', + 'ch.qos.logback.core.net.ssl.KeyStoreFactoryBean', + 'ch.qos.logback.core.net.ssl.SSLParametersConfiguration', + 'ch.qos.logback.core.net.ssl.SSLComponent', + 'ch.qos.logback.core.net.ssl.SSLNestedComponentRegistryRules', + 'ch.qos.logback.core.net.ssl.SSLConfigurableSocket', + 'ch.qos.logback.core.net.ssl.SSLConfigurableServerSocket', + 'ch.qos.logback.core.net.ssl.SSLConfiguration', + 'ch.qos.logback.core.net.ssl.ConfigurableSSLSocketFactory', + 'ch.qos.logback.core.net.ssl.ConfigurableSSLServerSocketFactory', + 'ch.qos.logback.core.net.ssl.SecureRandomFactoryBean', + 'ch.qos.logback.core.net.ssl.SSLContextFactoryBean', + 'ch.qos.logback.core.net.ssl.SSL', + 'ch.qos.logback.core.net.ssl.SSLConfigurable', + 'ch.qos.logback.core.net.ssl.TrustManagerFactoryFactoryBean', + 'ch.qos.logback.core.net.ssl.KeyManagerFactoryFactoryBean', + 'ch.qos.logback.core.net.SMTPAppenderBase', + 'ch.qos.logback.core.net.SyslogAppenderBase', + 'ch.qos.logback.core.net.SocketConnector', + 'ch.qos.logback.core.net.SyslogOutputStream', + 'ch.qos.logback.core.net.QueueFactory', + 'ch.qos.logback.core.net.HardenedObjectInputStream', + 'ch.qos.logback.core.net.AbstractSocketAppender', + 'ch.qos.logback.core.net.AbstractSSLSocketAppender', + 'ch.qos.logback.core.net.ObjectWriterFactory', + 'ch.qos.logback.core.net.ObjectWriter', + 'ch.qos.logback.core.net.AutoFlushingObjectWriter', + 'ch.qos.logback.core.net.SyslogConstants', + 'ch.qos.logback.core.net.server.ServerRunner', + 'ch.qos.logback.core.net.server.Client', + 'ch.qos.logback.core.net.server.ServerListener', + 'ch.qos.logback.core.net.server.RemoteReceiverStreamClient', + 'ch.qos.logback.core.net.server.AbstractServerSocketAppender', + 'ch.qos.logback.core.net.server.ClientVisitor', + 'ch.qos.logback.core.net.server.RemoteReceiverClient', + 'ch.qos.logback.core.net.server.RemoteReceiverServerRunner', + 'ch.qos.logback.core.net.server.SSLServerSocketAppenderBase', + 'ch.qos.logback.core.net.server.ConcurrentServerRunner', + 'ch.qos.logback.core.net.server.ServerSocketListener', + 'ch.qos.logback.core.net.server.RemoteReceiverServerListener', + 'ch.qos.logback.core.UnsynchronizedAppenderBase', + 'ch.qos.logback.core.AsyncAppenderBase', + 'ch.qos.logback.core.util.CloseUtil', + 'ch.qos.logback.core.util.DatePatternToRegexUtil', + 'ch.qos.logback.core.util.StatusListenerConfigHelper', + 'ch.qos.logback.core.util.SystemInfo', + 'ch.qos.logback.core.util.DefaultInvocationGate', + 'ch.qos.logback.core.util.CachingDateFormatter', + 'ch.qos.logback.core.util.InterruptUtil', + 'ch.qos.logback.core.util.LocationUtil', + 'ch.qos.logback.core.util.TimeUtil', + 'ch.qos.logback.core.util.COWArrayList', + 'ch.qos.logback.core.util.Loader', + 'ch.qos.logback.core.util.CharSequenceState', + 'ch.qos.logback.core.util.StatusPrinter', + 'ch.qos.logback.core.util.Duration', + 'ch.qos.logback.core.util.ContentTypeUtil', + 'ch.qos.logback.core.util.FileUtil', + 'ch.qos.logback.core.util.DynamicClassLoadingException', + 'ch.qos.logback.core.util.InvocationGate', + 'ch.qos.logback.core.util.OptionHelper', + 'ch.qos.logback.core.util.IncompatibleClassException', + 'ch.qos.logback.core.util.ExecutorServiceUtil', + 'ch.qos.logback.core.util.StringCollectionUtil', + 'ch.qos.logback.core.util.CharSequenceToRegexMapper', + 'ch.qos.logback.core.util.FixedDelay', + 'ch.qos.logback.core.util.FileSize', + 'ch.qos.logback.core.util.DelayStrategy', + 'ch.qos.logback.core.util.EnvUtil', + 'ch.qos.logback.core.util.ContextUtil', + 'ch.qos.logback.core.util.AggregationType', + 'ch.qos.logback.core.util.PropertySetterException', + 'ch.qos.logback.core.LifeCycleManager', + 'ch.qos.logback.core.LayoutBase', + 'ch.qos.logback.core.encoder.NonClosableInputStream', + 'ch.qos.logback.core.encoder.Encoder', + 'ch.qos.logback.core.encoder.ByteArrayUtil', + 'ch.qos.logback.core.encoder.EncoderBase', + 'ch.qos.logback.core.encoder.EchoEncoder', + 'ch.qos.logback.core.encoder.LayoutWrappingEncoder', + 'ch.qos.logback.core.recovery.RecoveryCoordinator', + 'ch.qos.logback.core.recovery.ResilientOutputStreamBase', + 'ch.qos.logback.core.recovery.ResilientSyslogOutputStream', + 'ch.qos.logback.core.recovery.ResilientFileOutputStream', + 'ch.qos.logback.core.AppenderBase', + 'ch.qos.logback.core.subst.Node', + 'ch.qos.logback.core.subst.Parser', + 'ch.qos.logback.core.subst.Token', + 'ch.qos.logback.core.subst.NodeToStringTransformer', + 'ch.qos.logback.core.subst.Tokenizer', + 'ch.qos.logback.core.FileAppender', + 'ch.qos.logback.core.sift.AppenderFactory', + 'ch.qos.logback.core.sift.SiftingAppenderBase', + 'ch.qos.logback.core.sift.SiftingJoranConfiguratorBase', + 'ch.qos.logback.core.sift.AbstractDiscriminator', + 'ch.qos.logback.core.sift.Discriminator', + 'ch.qos.logback.core.sift.AbstractAppenderFactoryUsingJoran', + 'ch.qos.logback.core.sift.AppenderTracker', + 'ch.qos.logback.core.sift.DefaultDiscriminator', + 'ch.qos.logback.core.html.CssBuilder', + 'ch.qos.logback.core.html.NOPThrowableRenderer', + 'ch.qos.logback.core.html.HTMLLayoutBase', + 'ch.qos.logback.core.html.IThrowableRenderer', + 'ch.qos.logback.core.rolling.TriggeringPolicyBase', + 'ch.qos.logback.core.rolling.helper.Compressor', + 'ch.qos.logback.core.rolling.helper.PeriodicityType', + 'ch.qos.logback.core.rolling.helper.TokenConverter', + 'ch.qos.logback.core.rolling.helper.IntegerTokenConverter', + 'ch.qos.logback.core.rolling.helper.CompressionMode', + 'ch.qos.logback.core.rolling.helper.ArchiveRemover', + 'ch.qos.logback.core.rolling.helper.FileFilterUtil', + 'ch.qos.logback.core.rolling.helper.RenameUtil', + 'ch.qos.logback.core.rolling.helper.DateTokenConverter', + 'ch.qos.logback.core.rolling.helper.FileNamePattern', + 'ch.qos.logback.core.rolling.helper.RollingCalendar', + 'ch.qos.logback.core.rolling.helper.FileStoreUtil', + 'ch.qos.logback.core.rolling.helper.SizeAndTimeBasedArchiveRemover', + 'ch.qos.logback.core.rolling.helper.TimeBasedArchiveRemover', + 'ch.qos.logback.core.rolling.helper.MonoTypedConverter', + 'ch.qos.logback.core.rolling.RollingPolicyBase', + 'ch.qos.logback.core.rolling.RollingFileAppender', + 'ch.qos.logback.core.rolling.FixedWindowRollingPolicy', + 'ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicyBase', + 'ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicy', + 'ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy', + 'ch.qos.logback.core.rolling.RollingPolicy', + 'ch.qos.logback.core.rolling.TimeBasedRollingPolicy', + 'ch.qos.logback.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy', + 'ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy', + 'ch.qos.logback.core.rolling.RolloverFailure', + 'ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP', + 'ch.qos.logback.core.rolling.TriggeringPolicy', + 'ch.qos.logback.core.pattern.ReplacingCompositeConverter', + 'ch.qos.logback.core.pattern.ConverterUtil', + 'ch.qos.logback.core.pattern.parser.Compiler', + 'ch.qos.logback.core.pattern.parser.Node', + 'ch.qos.logback.core.pattern.parser.Parser', + 'ch.qos.logback.core.pattern.parser.Token', + 'ch.qos.logback.core.pattern.parser.OptionTokenizer', + 'ch.qos.logback.core.pattern.parser.TokenStream', + 'ch.qos.logback.core.pattern.parser.CompositeNode', + 'ch.qos.logback.core.pattern.parser.FormattingNode', + 'ch.qos.logback.core.pattern.parser.SimpleKeywordNode', + 'ch.qos.logback.core.pattern.Converter', + 'ch.qos.logback.core.pattern.PatternLayoutEncoderBase', + 'ch.qos.logback.core.pattern.LiteralConverter', + 'ch.qos.logback.core.pattern.PostCompileProcessor', + 'ch.qos.logback.core.pattern.util.RegularEscapeUtil', + 'ch.qos.logback.core.pattern.util.AsIsEscapeUtil', + 'ch.qos.logback.core.pattern.util.AlmostAsIsEscapeUtil', + 'ch.qos.logback.core.pattern.util.IEscapeUtil', + 'ch.qos.logback.core.pattern.util.RestrictedEscapeUtil', + 'ch.qos.logback.core.pattern.SpacePadder', + 'ch.qos.logback.core.pattern.CompositeConverter', + 'ch.qos.logback.core.pattern.PatternLayoutBase', + 'ch.qos.logback.core.pattern.DynamicConverter', + 'ch.qos.logback.core.pattern.color.YellowCompositeConverter', + 'ch.qos.logback.core.pattern.color.ANSIConstants', + 'ch.qos.logback.core.pattern.color.BoldYellowCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldBlueCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldWhiteCompositeConverter', + 'ch.qos.logback.core.pattern.color.CyanCompositeConverter', + 'ch.qos.logback.core.pattern.color.MagentaCompositeConverter', + 'ch.qos.logback.core.pattern.color.BlueCompositeConverter', + 'ch.qos.logback.core.pattern.color.BlackCompositeConverter', + 'ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase', + 'ch.qos.logback.core.pattern.color.GrayCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldMagentaCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldCyanCompositeConverter', + 'ch.qos.logback.core.pattern.color.RedCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldGreenCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldRedCompositeConverter', + 'ch.qos.logback.core.pattern.color.GreenCompositeConverter', + 'ch.qos.logback.core.pattern.color.WhiteCompositeConverter', + 'ch.qos.logback.core.pattern.FormattingConverter', + 'ch.qos.logback.core.pattern.IdentityCompositeConverter', + 'ch.qos.logback.core.pattern.FormatInfo', + 'ch.qos.logback.core.OutputStreamAppender', + 'ch.qos.logback.core.boolex.JaninoEventEvaluatorBase', + 'ch.qos.logback.core.boolex.Matcher', + 'ch.qos.logback.core.boolex.EventEvaluatorBase', + 'ch.qos.logback.core.boolex.EvaluationException', + 'ch.qos.logback.core.boolex.EventEvaluator', + 'ch.qos.logback.core.read.CyclicBufferAppender', + 'ch.qos.logback.core.read.ListAppender', + 'ch.qos.logback.core.Context', + 'ch.qos.logback.core.ContextBase', + 'ch.qos.logback.core.status.StatusListenerAsList', + 'ch.qos.logback.core.status.StatusBase', + 'ch.qos.logback.core.status.NopStatusListener', + 'ch.qos.logback.core.status.StatusUtil', + 'ch.qos.logback.core.status.OnPrintStreamStatusListenerBase', + 'ch.qos.logback.core.status.StatusManager', + 'ch.qos.logback.core.status.ViewStatusMessagesServletBase', + 'ch.qos.logback.core.status.ErrorStatus', + 'ch.qos.logback.core.status.Status', + 'ch.qos.logback.core.status.StatusListener', + 'ch.qos.logback.core.status.InfoStatus', + 'ch.qos.logback.core.status.OnConsoleStatusListener', + 'ch.qos.logback.core.status.WarnStatus', + 'ch.qos.logback.core.status.OnErrorConsoleStatusListener', + 'ch.qos.logback.core.filter.EvaluatorFilter', + 'ch.qos.logback.core.filter.Filter', + 'ch.qos.logback.core.filter.AbstractMatcherFilter', + 'ch.qos.logback.core.Layout', + 'ch.qos.logback.classic.ViewStatusMessagesServlet', + 'ch.qos.logback.classic.ClassicConstants', + 'ch.qos.logback.classic.layout.TTLLLayout', + 'ch.qos.logback.classic.helpers.MDCInsertingServletFilter', + 'ch.qos.logback.classic.Level', + 'ch.qos.logback.classic.Level.off', + 'ch.qos.logback.classic.Level.error', + 'ch.qos.logback.classic.Level.warn', + 'ch.qos.logback.classic.Level.info', + 'ch.qos.logback.classic.Level.debug', + 'ch.qos.logback.classic.Level.trace', + 'ch.qos.logback.classic.Level.all,', + 'ch.qos.logback.classic.net.SSLSocketReceiver', + 'ch.qos.logback.classic.net.ReceiverBase', + 'ch.qos.logback.classic.net.SimpleSocketServer', + 'ch.qos.logback.classic.net.SimpleSSLSocketServer', + 'ch.qos.logback.classic.net.SocketNode', + 'ch.qos.logback.classic.net.SMTPAppender', + 'ch.qos.logback.classic.net.SocketReceiver', + 'ch.qos.logback.classic.net.SocketAcceptor', + 'ch.qos.logback.classic.net.SSLSocketAppender', + 'ch.qos.logback.classic.net.LoggingEventPreSerializationTransformer', + 'ch.qos.logback.classic.net.server.RemoteAppenderStreamClient', + 'ch.qos.logback.classic.net.server.RemoteAppenderServerListener', + 'ch.qos.logback.classic.net.server.SSLServerSocketAppender', + 'ch.qos.logback.classic.net.server.RemoteAppenderClient', + 'ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream', + 'ch.qos.logback.classic.net.server.ServerSocketAppender', + 'ch.qos.logback.classic.net.server.SSLServerSocketReceiver', + 'ch.qos.logback.classic.net.server.RemoteAppenderServerRunner', + 'ch.qos.logback.classic.net.server.ServerSocketReceiver', + 'ch.qos.logback.classic.net.SocketAppender', + 'ch.qos.logback.classic.net.SyslogAppender', + 'ch.qos.logback.classic.PatternLayout', + 'ch.qos.logback.classic.util.ContextSelectorStaticBinder', + 'ch.qos.logback.classic.util.StatusViaSLF4JLoggerFactory', + 'ch.qos.logback.classic.util.JNDIUtil', + 'ch.qos.logback.classic.util.LevelToSyslogSeverity', + 'ch.qos.logback.classic.util.LoggerNameUtil', + 'ch.qos.logback.classic.util.LogbackMDCAdapter', + 'ch.qos.logback.classic.util.CopyOnInheritThreadLocal', + 'ch.qos.logback.classic.util.ContextInitializer', + 'ch.qos.logback.classic.util.EnvUtil', + 'ch.qos.logback.classic.util.DefaultNestedComponentRules', + 'ch.qos.logback.classic.AsyncAppender', + 'ch.qos.logback.classic.jul.JULHelper', + 'ch.qos.logback.classic.jul.LevelChangePropagator', + 'ch.qos.logback.classic.encoder.PatternLayoutEncoder', + 'ch.qos.logback.classic.db.names.DBNameResolver', + 'ch.qos.logback.classic.db.names.ColumnName', + 'ch.qos.logback.classic.db.names.TableName', + 'ch.qos.logback.classic.db.names.DefaultDBNameResolver', + 'ch.qos.logback.classic.db.names.SimpleDBNameResolver', + 'ch.qos.logback.classic.log4j.XMLLayout', + 'ch.qos.logback.classic.LoggerContext', + 'ch.qos.logback.classic.turbo.TurboFilter', + 'ch.qos.logback.classic.turbo.MDCFilter', + 'ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter', + 'ch.qos.logback.classic.turbo.DuplicateMessageFilter', + 'ch.qos.logback.classic.turbo.MarkerFilter', + 'ch.qos.logback.classic.turbo.MDCValueLevelPair', + 'ch.qos.logback.classic.turbo.DynamicThresholdFilter', + 'ch.qos.logback.classic.turbo.MatchingFilter', + 'ch.qos.logback.classic.turbo.LRUMessageCache', + 'ch.qos.logback.classic.selector.servlet.LoggerContextFilter', + 'ch.qos.logback.classic.selector.servlet.ContextDetachingSCL', + 'ch.qos.logback.classic.selector.ContextJNDISelector', + 'ch.qos.logback.classic.selector.DefaultContextSelector', + 'ch.qos.logback.classic.selector.ContextSelector', + 'ch.qos.logback.classic.sift.MDCBasedDiscriminator', + 'ch.qos.logback.classic.sift.SiftingJoranConfigurator', + 'ch.qos.logback.classic.sift.JNDIBasedContextDiscriminator', + 'ch.qos.logback.classic.sift.AppenderFactoryUsingJoran', + 'ch.qos.logback.classic.sift.ContextBasedDiscriminator', + 'ch.qos.logback.classic.sift.SiftingAppender', + 'ch.qos.logback.classic.sift.SiftAction', + 'ch.qos.logback.classic.html.UrlCssBuilder', + 'ch.qos.logback.classic.html.HTMLLayout', + 'ch.qos.logback.classic.html.DefaultCssBuilder', + 'ch.qos.logback.classic.html.DefaultThrowableRenderer', + 'ch.qos.logback.classic.Logger', + 'ch.qos.logback.classic.pattern.ThrowableHandlingConverter', + 'ch.qos.logback.classic.pattern.ContextNameConverter', + 'ch.qos.logback.classic.pattern.LocalSequenceNumberConverter', + 'ch.qos.logback.classic.pattern.ClassOfCallerConverter', + 'ch.qos.logback.classic.pattern.PrefixCompositeConverter', + 'ch.qos.logback.classic.pattern.LineOfCallerConverter', + 'ch.qos.logback.classic.pattern.EnsureExceptionHandling', + 'ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviator', + 'ch.qos.logback.classic.pattern.FileOfCallerConverter', + 'ch.qos.logback.classic.pattern.LevelConverter', + 'ch.qos.logback.classic.pattern.ExtendedThrowableProxyConverter', + 'ch.qos.logback.classic.pattern.NamedConverter', + 'ch.qos.logback.classic.pattern.ClassicConverter', + 'ch.qos.logback.classic.pattern.NopThrowableInformationConverter', + 'ch.qos.logback.classic.pattern.RootCauseFirstThrowableProxyConverter', + 'ch.qos.logback.classic.pattern.MethodOfCallerConverter', + 'ch.qos.logback.classic.pattern.CallerDataConverter', + 'ch.qos.logback.classic.pattern.ClassNameOnlyAbbreviator', + 'ch.qos.logback.classic.pattern.MarkerConverter', + 'ch.qos.logback.classic.pattern.RelativeTimeConverter', + 'ch.qos.logback.classic.pattern.DateConverter', + 'ch.qos.logback.classic.pattern.PropertyConverter', + 'ch.qos.logback.classic.pattern.ThreadConverter', + 'ch.qos.logback.classic.pattern.LineSeparatorConverter', + 'ch.qos.logback.classic.pattern.MDCConverter', + 'ch.qos.logback.classic.pattern.color.HighlightingCompositeConverter', + 'ch.qos.logback.classic.pattern.ThrowableProxyConverter', + 'ch.qos.logback.classic.pattern.Abbreviator', + 'ch.qos.logback.classic.pattern.Util', + 'ch.qos.logback.classic.pattern.LoggerConverter', + 'ch.qos.logback.classic.pattern.SyslogStartConverter', + 'ch.qos.logback.classic.pattern.MessageConverter', + 'ch.qos.logback.classic.gaffer.GafferUtil', + 'ch.qos.logback.classic.boolex.OnMarkerEvaluator', + 'ch.qos.logback.classic.boolex.JaninoEventEvaluator', + 'ch.qos.logback.classic.boolex.OnErrorEvaluator', + 'ch.qos.logback.classic.boolex.GEventEvaluator', + 'ch.qos.logback.classic.boolex.IEvaluator', + 'ch.qos.logback.classic.filter.ThresholdFilter', + 'ch.qos.logback.classic.filter.LevelFilter', + 'java.lang.System', + 'java.lang.System.getenv', + 'java.lang.System.getProperty', + 'java.lang.System.getenv', + 'java.util.Map.getOrDefault', + 'com.kotlindiscord.kord.extensions.utils._EnvironmentKt.envOrNull', + 'org.quiltmc.community._ConstantsKt.getLOGGING_ENV', + 'org.quiltmc.community._ConstantsKt.getLOGGING_URL' +]