diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9890b0b0..fcc2b7cc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -31,7 +31,7 @@ body: id: logs attributes: label: Relevant log output - description: If your running your own LilyBot instance, please provide some logs that generated when your issue occured. This will be automatically formatted into code, so no need for backticks. + description: If your running your own LilyBot instance, please provide some logs that generated when your issue occurred. This will be automatically formatted into code, so no need for backticks. render: bash - type: input id: lily-version diff --git a/build.gradle.kts b/build.gradle.kts index 735bee2d..153686fb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,36 +1,31 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +@Suppress("DSL_SCOPE_VIOLATION") plugins { application - kotlin("jvm") - kotlin("plugin.serialization") - - id("com.github.johnrengelman.shadow") - id("io.gitlab.arturbosch.detekt") - id("com.github.jakemarsden.git-hooks") - id("org.ajoberstar.grgit") version "5.0.0" - id("net.kyori.blossom") version "1.3.1" + alias(libs.plugins.kotlin) + alias(libs.plugins.kotlinx.serialization) + alias(libs.plugins.shadow) + alias(libs.plugins.detekt) + alias(libs.plugins.git.hooks) + alias(libs.plugins.grgit) + alias(libs.plugins.blossom) } group = "org.hyacinthbots.lilybot" -version = "4.6.3" +version = "4.7.0" repositories { mavenCentral() maven { - name = "Kotlin Discord" - url = uri("https://maven.kotlindiscord.com/repository/maven-public/") - } - - maven { - name = "Sonatype Snapshots" + name = "Sonatype Snapshots (Legacy)" url = uri("https://oss.sonatype.org/content/repositories/snapshots") } maven { - name = "Sonatype Snapshots S01" + name = "Sonatype Snapshots" url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") } @@ -91,7 +86,7 @@ tasks { withType { kotlinOptions { jvmTarget = "17" - languageVersion = "1.7" + languageVersion = libs.plugins.kotlin.get().version.requiredVersion.substringBeforeLast(".") incremental = true freeCompilerArgs = listOf( "-opt-in=kotlin.RequiresOptIn" diff --git a/detekt.yml b/detekt.yml index b8d071b8..c0bfb92f 100644 --- a/detekt.yml +++ b/detekt.yml @@ -343,7 +343,7 @@ style: UnnecessaryLet: active: true UnnecessaryParentheses: - active: true + active: false # Conflicts with IntelliJ UntilInsteadOfRangeTo: active: true UnusedImports: diff --git a/docs/changelogs/4.x.x/4.4.2.md b/docs/changelogs/4.x.x/4.4.2.md index a6b24ee1..b9b6c421 100644 --- a/docs/changelogs/4.x.x/4.4.2.md +++ b/docs/changelogs/4.x.x/4.4.2.md @@ -7,7 +7,7 @@ New: * Added public logging to warning removal Change: -* Rename `messages` in ban & softban commands to `delete-message-days` +* Rename `messages` in ban & soft-ban commands to `delete-message-days` * Member counts return! Fix: diff --git a/docs/changelogs/4.x.x/4.7.0.md b/docs/changelogs/4.x.x/4.7.0.md new file mode 100644 index 00000000..6bc93263 --- /dev/null +++ b/docs/changelogs/4.x.x/4.7.0.md @@ -0,0 +1,20 @@ +# LilyBot 4.7.0 + +This update removes the deprecated support system and fixes a few bugs +You can find the full changelog below. + +New: +* Log deleted messages on ban in the same style as bulk message delete +* Add targeted server announcements + +Change: +* Update to Kotlin 1.8.0 +* Remove deprecated support system + +Fix: +* Detekt check for brackets, allowing for cleaner code +* Clean up many warnings +* Usage of `getMember()` rather than `getMemberOrNull()` leading to extra exceptions +* Update doc generator and kordex to fix some issues with errors + +You can find a list of all the commits in this update [here](https://github.com/hyacinthbots/LilyBot/compare/v4.6.3...v4.7.0) diff --git a/docs/commands.md b/docs/commands.md index af2df4e2..45afd334 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -34,17 +34,6 @@ Required Member Permissions: Manage Channels * **Arguments**: * `channel` - The channel to view the auto-threading settings for. - Channel ---- -#### Command name: `config support` -**Description**: Deprecated: Configure Lily's support system -Required Member Permissions: Manage Server - -* **Arguments**: - * `enable-support` - Whether to enable the support system - Boolean - * `custom-message` - True if you'd like to add a custom message, false if you'd like the default. - Boolean - * `support-channel` - The channel to be used for creating support threads in. - Optional Channel - * `support-role` - The role to add to support threads, when one is created. - Optional Role - --- #### Command name: `config moderation` **Description**: Configure Lily's moderation system @@ -134,7 +123,7 @@ None --- #### Command name: `github user` -**Description**: Search github for a User/Organisation +**Description**: Search GitHub for a User/Organisation * **Arguments**: * `username` - The name of the User/Organisation you wish to search for - String @@ -160,7 +149,8 @@ Description: Send an announcement to all guilds Lily is in **Required Member Permissions**: Administrator * Arguments: -None + * `target-guild` - The guild to send the announcement too - Optional Snowflake + --- ### Command name: `help` Description: Get help with using Lily! @@ -175,7 +165,7 @@ Description: Learn about Lily, and get uptime data! None --- ### Command name: `invite` -Description: Get an invite link for Lily! +Description: Get an invitation link for Lily! * Arguments: None @@ -287,7 +277,7 @@ Description: Bans a user. * `user` - Person to ban - User * `delete-message-days` - The number of days worth of messages to delete - Int * `reason` - The reason for the ban - Defaulting String - * `dm` - Whether to send a direct message to the user about the warn - Defaulting Boolean + * `dm` - Whether to send a direct message to the user about the ban - Defaulting Boolean * `image` - An image you'd like to provide as extra context for the action - Optional Attachment --- @@ -299,7 +289,7 @@ Description: Soft-bans a user. * `user` - Person to Soft ban - User * `delete-message-days` - The number of days worth of messages to delete - Optional Int/Long * `reason` - The reason for the ban - Defaulting String - * `dm` - Whether to send a direct message to the user about the warn - Defaulting Boolean + * `dm` - Whether to send a direct message to the user about the soft-ban - Defaulting Boolean * `image` - An image you'd like to provide as extra context for the action - Optional Attachment --- @@ -319,7 +309,7 @@ Description: Kicks a user. * Arguments: * `user` - Person to kick - User * `reason` - The reason for the Kick - Defaulting String - * `dm` - Whether to send a direct message to the user about the warn - Defaulting Boolean + * `dm` - Whether to send a direct message to the user about the kick - Defaulting Boolean * `image` - An image you'd like to provide as extra context for the action - Optional Attachment --- @@ -339,7 +329,7 @@ Description: Times out a user. * `user` - Person to timeout - User * `duration` - Duration of timeout - Coalescing Optional Duration * `reason` - Reason for timeout - Defaulting String - * `dm` - Whether to send a direct message to the user about the warn - Defaulting Boolean + * `dm` - Whether to send a direct message to the user about the timeout - Defaulting Boolean * `image` - An image you'd like to provide as extra context for the action - Optional Attachment --- @@ -358,8 +348,8 @@ Description: Warns a user. * Arguments: * `user` - Person to warn - User - * `reason` - Reason for warn - Defaulting String - * `dm` - Whether to send a direct message to the user about the warn - Defaulting Boolean + * `reason` - Reason for warning - Defaulting String + * `dm` - Whether to send a direct message to the user about the warning - Defaulting Boolean * `image` - An image you'd like to provide as extra context for the action - Optional Attachment --- @@ -369,7 +359,7 @@ Description: Removes a user's warnings * Arguments: * `user` - Person to remove warn from - User - * `dm` - Whether to send a direct message to the user about the warn - Defaulting Boolean + * `dm` - Whether to send a direct message to the user about the warning - Defaulting Boolean --- #### Command name: `news-publishing set` @@ -679,7 +669,7 @@ None --- ### Command name: `command-list` -Description: Shows a list of HyacinthDev2's commands! +Description: Shows a list of LilyBot's commands! * Arguments: None diff --git a/docs/usage-guide.md b/docs/usage-guide.md index 6282554f..2e38ff93 100644 --- a/docs/usage-guide.md +++ b/docs/usage-guide.md @@ -1,6 +1,6 @@ # Using LilyBot -This is a guide on how to **add the official instance of Lily to your sever**. +This is a guide on how to **add the official instance of Lily to your Discord sever**. If you're looking to set up a development environment for Lily, try our [development guide](https://github.com/HyacinthBots/LilyBot/blob/main/docs/development-guide.md). diff --git a/gradle.properties b/gradle.properties index 22058afa..cefb4964 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,4 @@ +# suppress inspection "UnusedProperty" for whole file # Gradle props org.gradle.jvmargs=-Xmx1536m -XX:MaxMetaspaceSize=1536m org.gradle.parallel=true diff --git a/libs.versions.toml b/libs.versions.toml index 395b3df6..cd62e84e 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -1,20 +1,28 @@ [versions] -kord-extensions = "1.5.6-20230102.101158-6" +# Plugins +kotlin = "1.8.0" +shadow = "7.1.2" +detekt = "1.22.0" +git-hooks = "0.0.2" +grgit = "5.0.0" +blossom = "1.3.1" + +# Libraries +kord-extensions = "1.5.6-20230208.121744-1" logging = "3.0.4" logback = "1.4.5" github-api = "1.313" kmongo = "4.8.0" -detekt = "1.22.0" cozy-welcome = "1.0-SNAPSHOT" -dma = "main-SNAPSHOT" -docgenerator = "0.1.1" +dma = "v0.2.0" +docgenerator = "0.1.2" [libraries] kord-extensions-core = { module = "com.kotlindiscord.kord.extensions:kord-extensions", version.ref = "kord-extensions" } kord-extensions-phishing = { module = "com.kotlindiscord.kord.extensions:extra-phishing", version.ref = "kord-extensions" } kord-extensions-pluralkit = { module = "com.kotlindiscord.kord.extensions:extra-pluralkit", version.ref = "kord-extensions"} kord-extensions-unsafe = { module = "com.kotlindiscord.kord.extensions:unsafe", version.ref = "kord-extensions"} -kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8" } +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib" } logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } logging = { module = "io.github.microutils:kotlin-logging", version.ref = "logging" } github-api = { module = "org.kohsuke:github-api", version.ref = "github-api" } @@ -23,3 +31,12 @@ detekt = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref cozy-welcome = {module = "org.quiltmc.community:module-welcome", version.ref = "cozy-welcome"} dma = { module = "org.hyacinthbots:discord-moderation-actions", version.ref = "dma"} docgenerator = { module = "org.hyacinthbots:doc-generator", version.ref = "docgenerator" } + +[plugins] +kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +git-hooks = { id = "com.github.jakemarsden.git-hooks", version.ref = "git-hooks" } +grgit = { id = "org.ajoberstar.grgit", version.ref = "grgit" } +blossom = { id = "net.kyori.blossom", version.ref = "blossom" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 51bf8f95..611c1f45 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,23 +1,7 @@ -pluginManagement { - plugins { - val kotlinVersion = "1.7.21" - kotlin("jvm") version kotlinVersion - kotlin("plugin.serialization") version kotlinVersion - - id("com.github.johnrengelman.shadow") version "7.1.2" - - id("io.gitlab.arturbosch.detekt") version "1.22.0" - - id("com.github.jakemarsden.git-hooks") version "0.0.2" - } - repositories { - gradlePluginPortal() - } -} - rootProject.name = "LilyBot" dependencyResolutionManagement { + @Suppress("UnstableApiUsage") versionCatalogs { create("libs") { from(files("libs.versions.toml")) diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/LilyBot.kt b/src/main/kotlin/org/hyacinthbots/lilybot/LilyBot.kt index 3f67add6..aefe2e9c 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/LilyBot.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/LilyBot.kt @@ -142,6 +142,7 @@ suspend fun main() { filePath = docFile environment = ENVIRONMENT useBuiltinCommandList = true + botName = "LilyBot" } // Connect to GitHub to allow the GitHub commands to function diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/Cleanups.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/Cleanups.kt index 3fa91174..57a8550f 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/Cleanups.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/Cleanups.kt @@ -16,7 +16,6 @@ import org.hyacinthbots.lilybot.database.collections.ModerationConfigCollection import org.hyacinthbots.lilybot.database.collections.NewsChannelPublishingCollection import org.hyacinthbots.lilybot.database.collections.ReminderCollection import org.hyacinthbots.lilybot.database.collections.RoleMenuCollection -import org.hyacinthbots.lilybot.database.collections.SupportConfigCollection import org.hyacinthbots.lilybot.database.collections.TagsCollection import org.hyacinthbots.lilybot.database.collections.ThreadsCollection import org.hyacinthbots.lilybot.database.collections.UtilityConfigCollection @@ -71,7 +70,6 @@ object Cleanups : KordExKoinComponent { NewsChannelPublishingCollection().clearAutoPublishingForGuild(it.guildId) ReminderCollection().removeGuildReminders(it.guildId) RoleMenuCollection().removeAllRoleMenus(it.guildId) - SupportConfigCollection().clearConfig(it.guildId) TagsCollection().clearTags(it.guildId) ThreadsCollection().removeGuildThreads(it.guildId) UtilityConfigCollection().clearConfig(it.guildId) diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/collections/ConfigCollection.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/collections/ConfigCollection.kt index 4edae095..61752bca 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/collections/ConfigCollection.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/collections/ConfigCollection.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION_ERROR") - package org.hyacinthbots.lilybot.database.collections import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent @@ -7,7 +5,6 @@ import dev.kord.common.entity.Snowflake import org.hyacinthbots.lilybot.database.Database import org.hyacinthbots.lilybot.database.entities.LoggingConfigData import org.hyacinthbots.lilybot.database.entities.ModerationConfigData -import org.hyacinthbots.lilybot.database.entities.SupportConfigData import org.hyacinthbots.lilybot.database.entities.UtilityConfigData import org.koin.core.component.inject import org.litote.kmongo.eq @@ -110,60 +107,6 @@ class ModerationConfigCollection : KordExKoinComponent { collection.deleteOne(ModerationConfigData::guildId eq inputGuildId) } -/** - * This class contains the functions for interacting with the [Support Config Database][SupportConfigData]. This class - * contains functions for getting, setting and removing support config. - * - * @since 4.0.0 - * @see getConfig - * @see setConfig - * @see clearConfig - */ -@Deprecated( - "Replaced with auto threading collection", - ReplaceWith("AutoThreadingCollection", "org.hyacinthbots.database.collections.AutoThreadingCollection"), - DeprecationLevel.WARNING -) -class SupportConfigCollection : KordExKoinComponent { - private val configDb: Database by inject() - - @PublishedApi - internal val collection = configDb.configDatabase.getCollection() - - /** - * Gets the support config for the given guild using the [guildId][inputGuildId]. - * - * @param inputGuildId The guild id to get the config for. - * @return The support config for the given guild. - * @author NoComment1105 - * @since 4.0.0 - */ - suspend inline fun getConfig(inputGuildId: Snowflake): SupportConfigData? = - collection.findOne(SupportConfigData::guildId eq inputGuildId) - - /** - * Adds the given [supportConfig] to the database. - * - * @param supportConfig The new config values for the support config you want to set. - * @author Miss Corruption - * @since 4.0.0 - */ - suspend inline fun setConfig(supportConfig: SupportConfigData) { - collection.deleteOne(SupportConfigData::guildId eq supportConfig.guildId) - collection.insertOne(supportConfig) - } - - /** - * Clears the support config for the given guild using the [guildId][inputGuildId]. - * - * @param inputGuildId The guild id to clear the config for. - * @author NoComment1105 - * @since 4.0.0 - */ - suspend inline fun clearConfig(inputGuildId: Snowflake) = - collection.deleteOne(SupportConfigData::guildId eq inputGuildId) -} - /** * This class contains the functions for interacting with the [Utility Config Database][UtilityConfigData]. * This class contains functions for getting, setting and removing Utility config. diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/entities/Config.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/entities/Config.kt index c571c766..a7aabe69 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/entities/Config.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/entities/Config.kt @@ -37,7 +37,7 @@ data class LoggingConfigData( * channel for logging and the team for pinging. * * @property guildId The ID of the guild the config is for - * @property enabled If the support module is enabled or not + * @property enabled If the moderation module is enabled or not * @property channel The ID of the action log for the guild * @property role The ID of the moderation role for the guild * @property quickTimeoutLength The length of timeout to apply when using the moderate menu @@ -56,31 +56,6 @@ data class ModerationConfigData( val publicLogging: Boolean?, ) -/** - * The data for support configuration. The support config stores the data for support functionality. Channel for the - * place to create threads to and team for pinging into support threads. - * - * @property guildId The ID of the guild the config is for - * @property enabled If the support module is enabled or not - * @property channel The ID of the support channel for the guild - * @property role The ID of the support team for the guild - * @property message The support message as a string, nullable - * @since 4.0.0 - */ -@Deprecated( - "Replace with AutoThreading data", - ReplaceWith("AutoThreadingData", "org.hyacinthbots.lilybot.database.entities.AutoThreadingData"), - DeprecationLevel.WARNING -) -@Serializable -data class SupportConfigData( - val guildId: Snowflake, - val enabled: Boolean, - val channel: Snowflake?, - val role: Snowflake?, - val message: String? -) - /** * The data for miscellaneous configuration. The miscellaneous config stores the data for enabling or disabling log * uploading. diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV1.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV1.kt index 6f99a20a..70e8f620 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV1.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV1.kt @@ -5,7 +5,7 @@ import org.litote.kmongo.coroutine.CoroutineDatabase import org.litote.kmongo.exists import org.litote.kmongo.setValue -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun configV1(configDb: CoroutineDatabase, mainDb: CoroutineDatabase) { with(configDb.getCollection("loggingConfigData")) { updateMany( diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV2.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV2.kt index f462d96e..b503c928 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV2.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV2.kt @@ -5,7 +5,7 @@ import org.litote.kmongo.coroutine.CoroutineDatabase import org.litote.kmongo.exists import org.litote.kmongo.setValue -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun configV2(db: CoroutineDatabase, mainDb: CoroutineDatabase) { with(db.getCollection("moderationConfigData")) { updateMany( diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV3.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV3.kt index 3a193ec6..ec5a5b98 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV3.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV3.kt @@ -5,7 +5,7 @@ import org.litote.kmongo.coroutine.CoroutineDatabase import org.litote.kmongo.exists import org.litote.kmongo.setValue -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun configV3(db: CoroutineDatabase, mainDb: CoroutineDatabase) { with(db.getCollection("loggingConfigData")) { updateMany( diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV4.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV4.kt index 05614d8b..dffdb802 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV4.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/config/configV4.kt @@ -1,14 +1,13 @@ package org.hyacinthbots.lilybot.database.migrations.config -import org.hyacinthbots.lilybot.database.entities.SupportConfigData -import org.hyacinthbots.lilybot.utils.utilsLogger import org.litote.kmongo.coroutine.CoroutineDatabase -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun configV4(db: CoroutineDatabase, mainDb: CoroutineDatabase) { - if (db.getCollection().find().toList().isEmpty()) { - db.dropCollection("supportConfigData") - } else { - utilsLogger.warn { "Support database was not empty!" } - } + // Support config has been removed. +// if (db.getCollection().find().toList().isEmpty()) { +// db.dropCollection("supportConfigData") +// } else { +// utilsLogger.warn { "Support database was not empty!" } +// } } diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV1.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV1.kt index fa13c1f7..5bf2f9eb 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV1.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV1.kt @@ -3,7 +3,7 @@ package org.hyacinthbots.lilybot.database.migrations.main import org.hyacinthbots.lilybot.database.entities.StatusData import org.litote.kmongo.coroutine.CoroutineDatabase -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") // This was commented out due to the remindme data class being removed suspend fun mainV1(db: CoroutineDatabase, configDb: CoroutineDatabase) { // val reminders = db.getCollection("remindMeData") diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV2.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV2.kt index bbdbaf56..1157c43d 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV2.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV2.kt @@ -5,7 +5,7 @@ import org.litote.kmongo.coroutine.CoroutineDatabase import org.litote.kmongo.exists import org.litote.kmongo.setValue -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun mainV2(db: CoroutineDatabase, configDb: CoroutineDatabase) { with(db.getCollection()) { updateMany(ThreadData::guildId exists false, setValue(ThreadData::guildId, null)) diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV3.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV3.kt index f74d6e06..a2a6ffca 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV3.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV3.kt @@ -2,7 +2,7 @@ package org.hyacinthbots.lilybot.database.migrations.main import org.litote.kmongo.coroutine.CoroutineDatabase -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun mainV3(db: CoroutineDatabase, configDb: CoroutineDatabase) { db.dropCollection("remindMeData") db.createCollection("reminderData") diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV4.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV4.kt index 0538a2d7..5f1aed1d 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV4.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV4.kt @@ -2,7 +2,7 @@ package org.hyacinthbots.lilybot.database.migrations.main import org.litote.kmongo.coroutine.CoroutineDatabase -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun mainV4(db: CoroutineDatabase, configDb: CoroutineDatabase) { db.createCollection("welcomeChannelData") db.createCollection("githubData") diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV5.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV5.kt index 1bd39e7f..af7d2382 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV5.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV5.kt @@ -1,9 +1,7 @@ package org.hyacinthbots.lilybot.database.migrations.main -import org.hyacinthbots.lilybot.database.entities.SupportConfigData import org.hyacinthbots.lilybot.database.entities.ThreadData import org.litote.kmongo.coroutine.CoroutineDatabase -import org.litote.kmongo.eq import org.litote.kmongo.exists import org.litote.kmongo.setValue @@ -15,10 +13,10 @@ suspend fun mainV5(db: CoroutineDatabase, configDb: CoroutineDatabase) { updateMany(ThreadData::parentChannelId exists false, setValue(ThreadData::parentChannelId, null)) } - with(configDb.getCollection()) { - for (it in find().toList()) { - if (it.channel == null) continue - // THIS MIGRATION IS COMPLETE. AWAY WITH THIS CODE IS FINE +// with(configDb.getCollection()) { +// for (it in find().toList()) { +// if (it.channel == null) continue + // THIS MIGRATION IS COMPLETE. AWAY WITH THIS CODE IS FINE // db.getCollection().insertOne( // AutoThreadingData( // it.guildId, @@ -31,7 +29,5 @@ suspend fun mainV5(db: CoroutineDatabase, configDb: CoroutineDatabase) { // it.message // ) // ) - configDb.getCollection().deleteOne(SupportConfigData::guildId eq it.guildId) - } - } +// configDb.getCollection().deleteOne(SupportConfigData::guildId eq it.guildId) } diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV6.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV6.kt index 8932e6a1..764978c0 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV6.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV6.kt @@ -5,7 +5,7 @@ import org.litote.kmongo.coroutine.CoroutineDatabase import org.litote.kmongo.exists import org.litote.kmongo.setValue -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun mainV6(db: CoroutineDatabase, configDb: CoroutineDatabase) { with(db.getCollection()) { updateMany(AutoThreadingData::addModsAndRole exists false, setValue(AutoThreadingData::addModsAndRole, false)) diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV7.kt b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV7.kt index ebfb41e8..ac8560dc 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV7.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/database/migrations/main/mainV7.kt @@ -2,7 +2,7 @@ package org.hyacinthbots.lilybot.database.migrations.main import org.litote.kmongo.coroutine.CoroutineDatabase -@Suppress("UnusedPrivateMember") +@Suppress("UnusedPrivateMember", "UNUSED_PARAMETER") suspend fun mainV7(db: CoroutineDatabase, configDb: CoroutineDatabase) { db.createCollection("newsChannelPublishingData") } diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/Config.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/Config.kt index 72c662c4..c74d4a08 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/Config.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/Config.kt @@ -24,17 +24,13 @@ import dev.kord.common.entity.Permission import dev.kord.core.behavior.channel.createMessage import dev.kord.core.behavior.getChannelOfOrNull import dev.kord.core.behavior.interaction.modal -import dev.kord.core.behavior.interaction.response.createEphemeralFollowup import dev.kord.core.entity.channel.TextChannel import dev.kord.rest.builder.message.EmbedBuilder import dev.kord.rest.builder.message.create.embed import kotlinx.datetime.Clock -import org.hyacinthbots.lilybot.database.collections.AutoThreadingCollection import org.hyacinthbots.lilybot.database.collections.LoggingConfigCollection import org.hyacinthbots.lilybot.database.collections.ModerationConfigCollection -import org.hyacinthbots.lilybot.database.collections.SupportConfigCollection import org.hyacinthbots.lilybot.database.collections.UtilityConfigCollection -import org.hyacinthbots.lilybot.database.entities.AutoThreadingData import org.hyacinthbots.lilybot.database.entities.LoggingConfigData import org.hyacinthbots.lilybot.database.entities.ModerationConfigData import org.hyacinthbots.lilybot.database.entities.PublicMemberLogData @@ -53,174 +49,6 @@ class Config : Extension() { name = "config" description = "Configure Lily's settings" - unsafeSubCommand(::SupportArgs) { - name = "support" - description = "Deprecated: Configure Lily's support system" - - initialResponse = InitialSlashCommandResponse.None - - requirePermission(Permission.ManageGuild) - - check { - anyGuild() - hasPermission(Permission.ManageGuild) - } - - val deprecationNotice = "This command is deprecated and will be removed in version v4.7.0! Please use" + - " the `/autothreading` command to fully configure thread inviting for a channel!" - - action { - val supportConfig = SupportConfigCollection().getConfig(guild!!.id) - if (supportConfig != null) { - ackEphemeral() - respondEphemeral { - content = "You already have a support configuration set. " + - "Please clear it before attempting to set a new one.\n$deprecationNotice" - } - return@action - } - - if (!arguments.enable) { - // We don't want this getting set because it doesn't get used - // SupportConfigCollection().setConfig(SupportConfigData(guild!!.id, false, null, null, null)) - ackEphemeral() - respondEphemeral { - content = "Support system disabled.\n$deprecationNotice" - } - return@action - } - - if (!canPingRole(arguments.role) && arguments.role != null) { - ackEphemeral() - respondEphemeral { - content = - "I cannot use the role: ${arguments.role!!.mention}, because it is not mentionable by " + - "regular users. Please enable this in the role settings, or use a different " + - "role.\n$deprecationNotice" - } - return@action - } - - val supportChannel: TextChannel? - if (arguments.enable && arguments.channel != null) { - supportChannel = guild!!.getChannelOfOrNull(arguments.channel!!.id) - if (supportChannel?.botHasPermissions( - Permission.ViewChannel, - Permission.SendMessages - ) != true - ) { - ackEphemeral() - respondEphemeral { - content = "The mod action log you've selected is invalid, or I can't view it. " + - "Please attempt to resolve this and try again.\n$deprecationNotice" - } - return@action - } - } - - suspend fun EmbedBuilder.supportEmbed() { - title = "Configuration: Support" - description = deprecationNotice - field { - name = "Support Team" - value = arguments.role?.mention ?: "Disabled" - } - field { - name = "Support Channel" - value = arguments.channel?.mention ?: "Disabled" - } - footer { - text = "Configured by: ${user.asUserOrNull()?.tag}" - } - } - - if (arguments.customMessage) { - val modalObj = SupportModal() - - this@unsafeSubCommand.componentRegistry.register(modalObj) - - event.interaction.modal( - modalObj.title, - modalObj.id - ) { - modalObj.applyToBuilder(this, getLocale(), null) - } - - modalObj.awaitCompletion { modalSubmitInteraction -> - interactionResponse = modalSubmitInteraction?.deferEphemeralMessageUpdate() - } - - interactionResponse?.createEphemeralFollowup { - embed { - supportEmbed() - field { - name = "Message" - value = modalObj.msgInput.value!! - } - } - } - - AutoThreadingCollection().setAutoThread( - AutoThreadingData( - guild!!.id, - arguments.channel?.id!!, - arguments.role?.id, - preventDuplicates = true, - archive = false, - contentAwareNaming = false, - mention = true, - creationMessage = modalObj.msgInput.value!!, - addModsAndRole = false - ) - ) - } else { - ackEphemeral() - respondEphemeral { - embed { - supportEmbed() - field { - name = "Message" - value = "default" - } - } - } - - AutoThreadingCollection().setAutoThread( - AutoThreadingData( - guild!!.id, - arguments.channel?.id!!, - arguments.role?.id, - preventDuplicates = true, - archive = false, - contentAwareNaming = false, - mention = true, - creationMessage = null, - addModsAndRole = false - ) - ) - } - - val utilityLog = getLoggingChannelWithPerms(ConfigOptions.UTILITY_LOG, this.getGuild()!!) - - if (utilityLog == null) { - respondEphemeral { - content = "Consider setting a utility config to log changes to configurations." - } - return@action - } - - utilityLog.createMessage { - embed { - supportEmbed() - field { - name = "Message" - value = SupportConfigCollection().getConfig(guild!!.id)?.message ?: "default" - } - } - } - } - } - ephemeralSubCommand(::ModerationArgs) { name = "moderation" description = "Configure Lily's moderation system" @@ -731,28 +559,6 @@ class Config : Extension() { } } - ConfigType.SUPPORT.name -> { - SupportConfigCollection().getConfig(guild!!.id) ?: run { - respond { - content = "No support configuration exists to clear!" - } - return@action - } - - logClear() - - SupportConfigCollection().clearConfig(guild!!.id) - respond { - embed { - title = "Config cleared: Support" - footer { - text = "Config cleared by ${user.asUserOrNull()?.tag}" - icon = user.asUserOrNull()?.avatar?.url - } - } - } - } - ConfigType.UTILITY.name -> { UtilityConfigCollection().getConfig(guild!!.id) ?: run { respond { @@ -778,7 +584,6 @@ class Config : Extension() { ConfigType.ALL.name -> { ModerationConfigCollection().clearConfig(guild!!.id) LoggingConfigCollection().clearConfig(guild!!.id) - SupportConfigCollection().clearConfig(guild!!.id) UtilityConfigCollection().clearConfig(guild!!.id) respond { embed { @@ -894,52 +699,6 @@ class Config : Extension() { } } - ConfigType.SUPPORT.name -> { - val config = SupportConfigCollection().getConfig(guild!!.id) - if (config == null) { - respond { - content = "There is no support config for this guild" - } - return@action - } - - respond { - embed { - title = "Current support config" - description = "This is the current support config for this guild" - field { - name = "Enabled/Disabled" - value = if (config.enabled) "Enabled" else "Disabled" - } - field { - name = "Channel" - value = "${config.channel?.let { guild!!.getChannelOrNull(it)?.mention }} " + - "${config.channel?.let { guild!!.getChannelOrNull(it)?.name }}" - } - field { - name = "Role" - value = "${config.role?.let { guild!!.getRoleOrNull(it)?.mention }} " + - "${config.role?.let { guild!!.getRoleOrNull(it)?.name }}" - } - field { - name = "Custom message" - value = - if (config.message != null) { - "${ - config.message.substring( - 0, - 500 - ) - } ..." - } else { - "Default" - } - } - timestamp = Clock.System.now() - } - } - } - ConfigType.UTILITY.name -> { val config = UtilityConfigCollection().getConfig(guild!!.id) if (config == null) { @@ -974,28 +733,6 @@ class Config : Extension() { } } - inner class SupportArgs : Arguments() { - val enable by boolean { - name = "enable-support" - description = "Whether to enable the support system" - } - - val customMessage by boolean { - name = "custom-message" - description = "True if you'd like to add a custom message, false if you'd like the default." - } - - val channel by optionalChannel { - name = "support-channel" - description = "The channel to be used for creating support threads in." - } - - val role by optionalRole { - name = "support-role" - description = "The role to add to support threads, when one is created." - } - } - inner class ModerationArgs : Arguments() { val enabled by boolean { name = "enable-moderation" @@ -1082,7 +819,6 @@ class Config : Extension() { name = "config-type" description = "The type of config to clear" choices = mutableMapOf( - "support" to ConfigType.SUPPORT.name, "moderation" to ConfigType.MODERATION.name, "logging" to ConfigType.LOGGING.name, "utility" to ConfigType.UTILITY.name, @@ -1096,7 +832,6 @@ class Config : Extension() { name = "config-type" description = "The type of config to clear" choices = mutableMapOf( - "support" to ConfigType.SUPPORT.name, "moderation" to ConfigType.MODERATION.name, "logging" to ConfigType.LOGGING.name, "utility" to ConfigType.UTILITY.name, @@ -1104,16 +839,6 @@ class Config : Extension() { } } - inner class SupportModal : ModalForm() { - override var title = "Support Message Configuration" - - val msgInput = paragraphText { - label = "Support Message" - placeholder = "Input the content of the message you would like sent when a support thread is created" - required = true - } - } - inner class LoggingModal : ModalForm() { override var title = "Public logging configuration" diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/ConfigOptions.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/ConfigOptions.kt index a5a196e5..4d797142 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/ConfigOptions.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/ConfigOptions.kt @@ -6,18 +6,6 @@ package org.hyacinthbots.lilybot.extensions.config * @since 4.0.0 */ enum class ConfigOptions { - /** The option that stores whether the support config is enabled or not. */ - @Deprecated("Support config is deprecated and will be removed in a future update") - SUPPORT_ENABLED, - - /** The option that stores the support channel. */ - @Deprecated("Support config is deprecated and will be removed in a future update") - SUPPORT_CHANNEL, - - /** The option that stores the support team role. */ - @Deprecated("Support config is deprecated and will be removed in a future update") - SUPPORT_ROLE, - /** The options that stores whether the moderation config is enabled or not.*/ MODERATION_ENABLED, diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/ConfigType.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/ConfigType.kt index b08ae2ff..894eecdd 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/ConfigType.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/config/ConfigType.kt @@ -6,10 +6,6 @@ package org.hyacinthbots.lilybot.extensions.config * @since 4.0.0 */ enum class ConfigType { - /** The entry for the support config. */ - @Deprecated("Support configuration is deprecated and will be removed in a future update.") - SUPPORT, - /** The entry for the moderation config. */ MODERATION, diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/AutoThreading.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/AutoThreading.kt index 5797dc88..41512d9c 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/AutoThreading.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/AutoThreading.kt @@ -490,7 +490,7 @@ class AutoThreading : Extension() { message: Message?, proxiedMessage: PKMessage? = null ) { - val memberFromPk = if (proxiedMessage != null) event.getGuild().getMember(proxiedMessage.sender) else null + val memberFromPk = if (proxiedMessage != null) event.getGuild().getMemberOrNull(proxiedMessage.sender) else null val channel: TextChannel = if (proxiedMessage == null) { message?.channel?.asChannelOfOrNull() ?: return diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/LogUploading.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/LogUploading.kt index 72759b47..8bbea9af 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/LogUploading.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/LogUploading.kt @@ -5,7 +5,6 @@ import com.kotlindiscord.kord.extensions.DISCORD_PINK import com.kotlindiscord.kord.extensions.DISCORD_RED import com.kotlindiscord.kord.extensions.checks.anyGuild import com.kotlindiscord.kord.extensions.checks.channelFor -import com.kotlindiscord.kord.extensions.checks.guildFor import com.kotlindiscord.kord.extensions.checks.hasPermission import com.kotlindiscord.kord.extensions.commands.application.slash.ephemeralSubCommand import com.kotlindiscord.kord.extensions.components.components @@ -43,11 +42,10 @@ import kotlinx.datetime.Clock import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import org.hyacinthbots.lilybot.database.collections.AutoThreadingCollection import org.hyacinthbots.lilybot.database.collections.LogUploadingBlacklistCollection -import org.hyacinthbots.lilybot.database.collections.SupportConfigCollection import org.hyacinthbots.lilybot.database.collections.ThreadsCollection import org.hyacinthbots.lilybot.database.collections.UtilityConfigCollection -import org.hyacinthbots.lilybot.database.entities.SupportConfigData import org.hyacinthbots.lilybot.extensions.config.ConfigOptions import org.hyacinthbots.lilybot.utils.botHasChannelPerms import org.hyacinthbots.lilybot.utils.configIsUsable @@ -99,14 +97,9 @@ class LogUploading : Extension() { } var deferUploadUntilThread = false - var supportConfig: SupportConfigData? = null - if (configIsUsable(ConfigOptions.SUPPORT_ENABLED, event.guildId!!) && - configIsUsable(ConfigOptions.SUPPORT_CHANNEL, event.guildId!!) - ) { - supportConfig = SupportConfigCollection().getConfig(guildFor(event)!!.id)!! - if (supportConfig.enabled && event.message.channel.id == supportConfig.channel) { - deferUploadUntilThread = true - } + val autoThreadingConfig = AutoThreadingCollection().getSingleAutoThread(event.message.channelId) + if (autoThreadingConfig != null && autoThreadingConfig.channelId == event.message.channelId) { + deferUploadUntilThread = true } val eventMessage = event.message.asMessageOrNull() // Get the message @@ -118,7 +111,7 @@ class LogUploading : Extension() { ThreadsCollection().getOwnerThreads(eventMember!!.id).forEach { val thread = event.getGuildOrNull()?.getChannelOfOrNull(it.threadId) ?: return@forEach - if (thread.parentId == supportConfig?.channel) { + if (thread.parentId == autoThreadingConfig?.channelId) { uploadChannel = event.getGuildOrNull()?.getChannelOfOrNull(it.threadId) ?: return@forEach @@ -148,7 +141,7 @@ class LogUploading : Extension() { gis.readAllBytes().decodeToString() } - // Ask the user to remove NEC to ease the debugging on the support team + // Ask the user to remove NEC to ease the debugging on mobile users and others val necText = "at Not Enough Crashes" val indexOfNECText = logContent.indexOf(necText) if (indexOfNECText != -1) { @@ -173,7 +166,7 @@ class LogUploading : Extension() { title = "Do you want to upload this file to mclo.gs?" description = "mclo.gs is a website that allows users to share minecraft logs " + - "through public posts.\nIt's easier for the support team to view " + + "through public posts.\nIt's easier for the mobile users to view " + "the file on mclo.gs, do you want it to be uploaded?" footer { text = diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/MessageDelete.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/MessageDelete.kt index dc17fc08..33a0662e 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/MessageDelete.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/events/MessageDelete.kt @@ -15,12 +15,11 @@ import dev.kord.core.entity.channel.GuildMessageChannel import dev.kord.core.event.message.MessageBulkDeleteEvent import dev.kord.rest.builder.message.create.embed import io.ktor.client.request.forms.ChannelProvider -import io.ktor.utils.io.jvm.javaio.toByteReadChannel +import io.ktor.util.cio.toByteReadChannel import kotlinx.datetime.Clock -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toLocalDateTime import org.hyacinthbots.lilybot.extensions.config.ConfigOptions import org.hyacinthbots.lilybot.utils.attachmentsAndProxiedMessageInfo +import org.hyacinthbots.lilybot.utils.generateBulkDeleteFile import org.hyacinthbots.lilybot.utils.getLoggingChannelWithPerms import org.hyacinthbots.lilybot.utils.ifNullOrEmpty import org.hyacinthbots.lilybot.utils.requiredConfigs @@ -84,12 +83,7 @@ class MessageDelete : Extension() { val messageLog = getLoggingChannelWithPerms(ConfigOptions.MESSAGE_LOG, event.getGuild()!!) ?: return@action - val messages = "# Messages\n\n**Total:** ${event.messages.size}\n\n" + - event.messages.reversed().joinToString("\n") { // Reversed for chronology - "* [${ - it.timestamp.toLocalDateTime(TimeZone.UTC).toString().replace("T", " @ ") - } UTC] **${it.author?.username}** (${it.author?.id}) » ${it.content}" - } + val messages = generateBulkDeleteFile(event.messages) messageLog.createMessage { embed { diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/moderation/LockingCommands.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/moderation/LockingCommands.kt index 12d82d37..d7230883 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/moderation/LockingCommands.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/moderation/LockingCommands.kt @@ -10,6 +10,7 @@ import com.kotlindiscord.kord.extensions.commands.converters.impl.defaultingStri import com.kotlindiscord.kord.extensions.commands.converters.impl.optionalChannel import com.kotlindiscord.kord.extensions.extensions.Extension import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand +import com.kotlindiscord.kord.extensions.types.EphemeralInteractionContext import com.kotlindiscord.kord.extensions.types.respond import dev.kord.common.entity.Permission import dev.kord.common.entity.Permissions @@ -17,6 +18,7 @@ import dev.kord.core.behavior.channel.asChannelOfOrNull import dev.kord.core.behavior.channel.createEmbed import dev.kord.core.behavior.channel.editRolePermission import dev.kord.core.behavior.edit +import dev.kord.core.entity.channel.Channel import dev.kord.core.entity.channel.TextChannel import dev.kord.core.entity.channel.thread.TextChannelThread import kotlinx.datetime.Clock @@ -61,17 +63,8 @@ class LockingCommands : Extension() { action { val channelArg = arguments.channel ?: event.interaction.getChannelOrNull() - var channelParent: TextChannel? = null - if (channelArg is TextChannelThread) { - channelParent = channelArg.getParent() - } - val targetChannel = channelParent ?: channelArg?.asChannelOfOrNull() - if (targetChannel == null) { - respond { - content = "I can't fetch the targeted channel properly." - return@action - } - } + val channelParent = getChannelParent(channelArg) + val targetChannel = getTargetChannel(channelParent, channelArg) val channelPerms = targetChannel!!.getPermissionOverwritesForRole(guild!!.id) if (channelPerms != null && channelPerms.denied.contains(Permission.SendMessages)) { @@ -204,17 +197,8 @@ class LockingCommands : Extension() { action { val channelArg = arguments.channel ?: event.interaction.getChannelOrNull() - var channelParent: TextChannel? = null - if (channelArg is TextChannelThread) { - channelParent = channelArg.getParent() - } - val targetChannel = channelParent ?: channelArg?.asChannelOfOrNull() - if (targetChannel == null) { - respond { - content = "I can't fetch the targeted channel properly." - return@action - } - } + val channelParent = getChannelParent(channelArg) + val targetChannel = getTargetChannel(channelParent, channelArg) val everyoneRole = guild!!.getRoleOrNull(guild!!.id) if (everyoneRole == null) { @@ -321,6 +305,49 @@ class LockingCommands : Extension() { } } + /** + * Gets the parent of the channel argument. + * + * @param channelArg The channel to get the parent of + * @return The channel parent as a [TextChannel] + * + * @author NoComment1105 + * @since 4.8.0 + */ + private suspend inline fun getChannelParent(channelArg: Channel?): TextChannel? { + var channelParent: TextChannel? = null + if (channelArg is TextChannelThread) { + channelParent = channelArg.getParent() + } + + return channelParent + } + + /** + * Gets the target channel and responds appropriately if unable to get it. + * + * @param channelParent The parent channel if that is the target + * @param channelArg The channel argument, if that is the target + * @return The channel as a [TextChannel] + * + * @author NoComment1105 + * @since 4.8.0 + */ + private suspend inline fun EphemeralInteractionContext.getTargetChannel( + channelParent: TextChannel?, + channelArg: Channel? + ): TextChannel? { + val targetChannel = channelParent ?: channelArg?.asChannelOfOrNull() + if (targetChannel == null) { + respond { + content = "I can't fetch the targeted channel properly." + return null + } + } + + return targetChannel + } + inner class LockChannelArgs : Arguments() { /** The channel that the user wants to lock. */ val channel by optionalChannel { diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/moderation/ModerationCommands.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/moderation/ModerationCommands.kt index a958355e..d3dc0270 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/moderation/ModerationCommands.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/moderation/ModerationCommands.kt @@ -2,6 +2,7 @@ package org.hyacinthbots.lilybot.extensions.moderation import com.kotlindiscord.kord.extensions.DISCORD_BLACK import com.kotlindiscord.kord.extensions.DISCORD_GREEN +import com.kotlindiscord.kord.extensions.DISCORD_RED import com.kotlindiscord.kord.extensions.annotations.DoNotChain import com.kotlindiscord.kord.extensions.checks.anyGuild import com.kotlindiscord.kord.extensions.checks.hasPermission @@ -46,6 +47,8 @@ import dev.kord.core.supplier.EntitySupplyStrategy import dev.kord.rest.builder.message.EmbedBuilder import dev.kord.rest.builder.message.create.embed import dev.kord.rest.request.KtorRequestException +import io.ktor.client.request.forms.ChannelProvider +import io.ktor.util.cio.toByteReadChannel import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import kotlinx.datetime.Clock @@ -65,8 +68,10 @@ import org.hyacinthbots.lilybot.extensions.config.ConfigOptions import org.hyacinthbots.lilybot.utils.baseModerationEmbed import org.hyacinthbots.lilybot.utils.botHasChannelPerms import org.hyacinthbots.lilybot.utils.dmNotificationStatusEmbedField +import org.hyacinthbots.lilybot.utils.generateBulkDeleteFile import org.hyacinthbots.lilybot.utils.getDmResult import org.hyacinthbots.lilybot.utils.getLoggingChannelWithPerms +import org.hyacinthbots.lilybot.utils.getMessagesForBanDelete import org.hyacinthbots.lilybot.utils.interval import org.hyacinthbots.lilybot.utils.isBotOrModerator import org.hyacinthbots.lilybot.utils.requiredConfigs @@ -488,6 +493,10 @@ class ModerationCommands : Extension() { return@action } + val fullSet = getMessagesForBanDelete(guild, arguments.userArgument, arguments.messages, event.kord) + + val messages = if (fullSet.isNotEmpty()) generateBulkDeleteFile(fullSet) else null + val action = ban(arguments.userArgument) { reason = arguments.reason logPublicly = ModerationConfigCollection().getConfig(guild!!.id)?.publicLogging @@ -497,23 +506,34 @@ class ModerationCommands : Extension() { deleteMessageDuration = DateTimePeriod(days = arguments.messages) this.loggingChannel = getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, guild!!) actionEmbed { - title = "Banned a user" - description = "${arguments.userArgument.mention} has been banned!" - image = arguments.image?.url - baseModerationEmbed(arguments.reason, arguments.userArgument, user) - dmNotificationStatusEmbedField(dmResult) - timestamp = Clock.System.now() - field { - name = "Days of messages deleted:" - value = arguments.messages.toString() - inline = false + embed { + title = "Banned a user" + description = "${arguments.userArgument.mention} has been banned!" + baseModerationEmbed(arguments.reason, arguments.userArgument, user) + image = arguments.image?.url + + dmNotificationStatusEmbedField(dmResult) + timestamp = Clock.System.now() + field { + name = "Days of messages deleted:" + value = arguments.messages.toString() + inline = false + } + } + if (messages != null) { + addFile( + "messages.md", + ChannelProvider { messages.byteInputStream().toByteReadChannel() } + ) } } publicActionEmbed { - title = "Banned a user" - description = "${arguments.userArgument.mention} has been banned!" - color = DISCORD_BLACK + embed { + title = "Banned a user" + description = "${arguments.userArgument.mention} has been banned!" + color = DISCORD_BLACK + } } dmEmbed { @@ -555,6 +575,10 @@ class ModerationCommands : Extension() { return@action } + val fullSet = getMessagesForBanDelete(guild, arguments.userArgument, arguments.messages, event.kord) + + val messages = if (fullSet.isNotEmpty()) generateBulkDeleteFile(fullSet) else null + val action = softban(arguments.userArgument) { reason = arguments.reason logPublicly = ModerationConfigCollection().getConfig(guild!!.id)?.publicLogging @@ -564,22 +588,32 @@ class ModerationCommands : Extension() { if (arguments.messages != null) deleteMessageDuration = DateTimePeriod(days = arguments.messages!!) loggingChannel = getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, guild!!) actionEmbed { - title = "Soft-Banned a user" - description = "${arguments.userArgument.mention} has been soft-banned!" - image = arguments.image?.url - baseModerationEmbed(arguments.reason, arguments.userArgument, user) - dmNotificationStatusEmbedField(dmResult) - timestamp = Clock.System.now() - field { - name = "Days of messages deleted" - value = "${arguments.messages ?: deleteMessageDuration.days}" - inline = false + embed { + title = "Soft-Banned a user" + description = "${arguments.userArgument.mention} has been soft-banned!" + image = arguments.image?.url + baseModerationEmbed(arguments.reason, arguments.userArgument, user) + dmNotificationStatusEmbedField(dmResult) + timestamp = Clock.System.now() + field { + name = "Days of messages deleted" + value = "${arguments.messages ?: deleteMessageDuration.days}" + inline = false + } + } + if (messages != null) { + addFile( + "messages.md", + ChannelProvider { messages.byteInputStream().toByteReadChannel() } + ) } } publicActionEmbed { - title = "Soft-Banned a user" - description = "${arguments.userArgument.mention} has been soft-banned!" + embed { + title = "Soft-Banned a user" + description = "${arguments.userArgument.mention} has been soft-banned!" + } } dmEmbed { @@ -618,20 +652,22 @@ class ModerationCommands : Extension() { loggingChannel = getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, guild!!) actionEmbed { - title = "Unbanned a user" - description = "${arguments.userArgument.mention} has been unbanned!\n${ - arguments.userArgument.id - } (${arguments.userArgument.tag})" - field { - name = "Reason:" - value = arguments.reason - } - footer { - text = user.asUserOrNull()?.tag ?: "Unable to get user tag" - icon = user.asUserOrNull()?.avatar?.url + embed { + title = "Unbanned a user" + description = "${arguments.userArgument.mention} has been unbanned!\n${ + arguments.userArgument.id + } (${arguments.userArgument.tag})" + field { + name = "Reason:" + value = arguments.reason + } + footer { + text = user.asUserOrNull()?.tag ?: "Unable to get user tag" + icon = user.asUserOrNull()?.avatar?.url + } + timestamp = Clock.System.now() + color = DISCORD_GREEN } - timestamp = Clock.System.now() - color = DISCORD_GREEN } } @@ -672,17 +708,21 @@ class ModerationCommands : Extension() { loggingChannel = getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, guild!!) actionEmbed { - title = "Kicked a user" - description = "${arguments.userArgument.mention} has been kicked!" - image = arguments.image?.url - baseModerationEmbed(arguments.reason, arguments.userArgument, user) - dmNotificationStatusEmbedField(dmResult) - timestamp = Clock.System.now() + embed { + title = "Kicked a user" + description = "${arguments.userArgument.mention} has been kicked!" + image = arguments.image?.url + baseModerationEmbed(arguments.reason, arguments.userArgument, user) + dmNotificationStatusEmbedField(dmResult) + timestamp = Clock.System.now() + } } publicActionEmbed { - title = "Kicked a user" - description = "${arguments.userArgument.mention} has been kicked!" + embed { + title = "Kicked a user" + description = "${arguments.userArgument.mention} has been kicked!" + } } dmEmbed { @@ -782,25 +822,29 @@ class ModerationCommands : Extension() { sendDm = arguments.dm loggingChannel = getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, guild!!) actionEmbed { - title = "Timeout" - image = arguments.image?.url - baseModerationEmbed(arguments.reason, arguments.userArgument, user) - dmNotificationStatusEmbedField(dmResult) - timestamp = Clock.System.now() - field { - name = "Duration:" - value = duration.toDiscord(TimestampType.Default) + " (${durationArg.interval()})" - inline = false + embed { + title = "Timeout" + image = arguments.image?.url + baseModerationEmbed(arguments.reason, arguments.userArgument, user) + dmNotificationStatusEmbedField(dmResult) + timestamp = Clock.System.now() + field { + name = "Duration:" + value = duration.toDiscord(TimestampType.Default) + " (${durationArg.interval()})" + inline = false + } } } publicActionEmbed { - title = "Timeout" - description = "${arguments.userArgument.mention} was timed out by a moderator" - color = DISCORD_BLACK - field { - name = "Duration:" - value = duration.toDiscord(TimestampType.Default) + " (${durationArg.interval()})" - inline = false + embed { + title = "Timeout" + description = "${arguments.userArgument.mention} was timed out by a moderator" + color = DISCORD_BLACK + field { + name = "Duration:" + value = duration.toDiscord(TimestampType.Default) + " (${durationArg.interval()})" + inline = false + } } } dmEmbed { @@ -838,19 +882,21 @@ class ModerationCommands : Extension() { sendDm = arguments.dm loggingChannel = getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, guild!!) actionEmbed { - title = "Timeout Removed" - dmNotificationStatusEmbedField(dmResult) - field { - name = "User:" - value = "${arguments.userArgument.tag} \n${arguments.userArgument.id}" - inline = false - } - footer { - text = "Requested by ${user.asUserOrNull()?.tag}" - icon = user.asUserOrNull()?.avatar?.url + embed { + title = "Timeout Removed" + dmNotificationStatusEmbedField(dmResult) + field { + name = "User:" + value = "${arguments.userArgument.tag} \n${arguments.userArgument.id}" + inline = false + } + footer { + text = "Requested by ${user.asUserOrNull()?.tag}" + icon = user.asUserOrNull()?.avatar?.url + } + timestamp = Clock.System.now() + color = DISCORD_BLACK } - timestamp = Clock.System.now() - color = DISCORD_BLACK } dmEmbed { title = "Timeout removed in ${guild!!.asGuildOrNull()?.name}" @@ -940,6 +986,7 @@ class ModerationCommands : Extension() { name = "Total strikes" value = strikes.toString() } + color = DISCORD_RED } if (config.autoPunishOnWarn == true && strikes != 1) { embed { @@ -957,7 +1004,7 @@ class ModerationCommands : Extension() { channel.createEmbed { title = "Warning" description = "${arguments.userArgument.mention} has been warned by a moderator" - color = DISCORD_BLACK + color = DISCORD_RED } } } @@ -1014,7 +1061,7 @@ class ModerationCommands : Extension() { val actionLog = getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, this.getGuild()!!) ?: return@action actionLog.createEmbed { title = "Warning Removal" - color = DISCORD_BLACK + color = DISCORD_GREEN timestamp = Clock.System.now() baseModerationEmbed(null, targetUser, user) dmNotificationStatusEmbedField(dmResult) @@ -1029,7 +1076,7 @@ class ModerationCommands : Extension() { channel.createEmbed { title = "Warning Removal" description = "${arguments.userArgument.mention} had a warn strike removed by a moderator." - color = DISCORD_BLACK + color = DISCORD_GREEN } } } @@ -1059,7 +1106,7 @@ class ModerationCommands : Extension() { /** Whether to DM the user or not. */ val dm by defaultingBoolean { name = "dm" - description = "Whether to send a direct message to the user about the warn" + description = "Whether to send a direct message to the user about the ban" defaultValue = true } @@ -1093,7 +1140,7 @@ class ModerationCommands : Extension() { /** Whether to DM the user or not. */ val dm by defaultingBoolean { name = "dm" - description = "Whether to send a direct message to the user about the warn" + description = "Whether to send a direct message to the user about the soft-ban" defaultValue = true } @@ -1136,7 +1183,7 @@ class ModerationCommands : Extension() { /** Whether to DM the user or not. */ val dm by defaultingBoolean { name = "dm" - description = "Whether to send a direct message to the user about the warn" + description = "Whether to send a direct message to the user about the kick" defaultValue = true } @@ -1178,7 +1225,7 @@ class ModerationCommands : Extension() { /** Whether to DM the user or not. */ val dm by defaultingBoolean { name = "dm" - description = "Whether to send a direct message to the user about the warn" + description = "Whether to send a direct message to the user about the timeout" defaultValue = true } @@ -1214,14 +1261,14 @@ class ModerationCommands : Extension() { /** The reason for the warning. */ val reason by defaultingString { name = "reason" - description = "Reason for warn" + description = "Reason for warning" defaultValue = "No reason provided" } /** Whether to DM the user or not. */ val dm by defaultingBoolean { name = "dm" - description = "Whether to send a direct message to the user about the warn" + description = "Whether to send a direct message to the user about the warning" defaultValue = true } @@ -1242,7 +1289,7 @@ class ModerationCommands : Extension() { /** Whether to DM the user or not. */ val dm by defaultingBoolean { name = "dm" - description = "Whether to send a direct message to the user about the warn" + description = "Whether to send a direct message to the user about the warning" defaultValue = true } } diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/Github.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/Github.kt index 18529cdc..3438a992 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/Github.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/Github.kt @@ -393,7 +393,7 @@ class Github : Extension() { publicSubCommand(::UserArgs) { name = "user" - description = "Search github for a User/Organisation" + description = "Search GitHub for a User/Organisation" action { val ghUser: GHUser? diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/GuildAnnouncements.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/GuildAnnouncements.kt index 3c86085a..804f245a 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/GuildAnnouncements.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/GuildAnnouncements.kt @@ -1,6 +1,8 @@ package org.hyacinthbots.lilybot.extensions.util import com.kotlindiscord.kord.extensions.checks.hasPermission +import com.kotlindiscord.kord.extensions.commands.Arguments +import com.kotlindiscord.kord.extensions.commands.converters.impl.optionalSnowflake import com.kotlindiscord.kord.extensions.components.components import com.kotlindiscord.kord.extensions.components.ephemeralButton import com.kotlindiscord.kord.extensions.components.forms.ModalForm @@ -24,7 +26,7 @@ class GuildAnnouncements : Extension() { override val name = "guild-announcements" override suspend fun setup() { - ephemeralSlashCommand(::GuildAnnouncementModal) { + ephemeralSlashCommand(::GuildAnnouncementArgs, ::GuildAnnouncementModal) { name = "announcement" description = "Send an announcement to all guilds Lily is in" @@ -38,33 +40,65 @@ class GuildAnnouncements : Extension() { action { modal -> var response: EphemeralFollowupMessage? = null response = respond { - content = "Would you like to send this message? It will be delivered to all servers this bot is in." + content = "Would you like to send this message? " + + if (arguments.targetGuild == null) { + "It will be delivered to all servers this bot is in." + } else { + "It will be delivered to your specified target guild: ${arguments.targetGuild}" + } components { ephemeralButton { label = "Yes" style = ButtonStyle.Success - action { + action ButtonAction@{ response?.edit { content = "Message sent!" components { removeAll() } } - event.kord.guilds.toList().chunked(15).forEach { chunk -> - for (i in chunk) { - val channel = - getLoggingChannelWithPerms(ConfigOptions.UTILITY_LOG, i) - ?: getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, i) - ?: getSystemChannelWithPerms(i) - ?: getFirstUsableChannel(i) - ?: continue - - channel.createEmbed { - title = modal?.header?.value - description = modal?.body?.value - color = Color(0x7B52AE) - footer { - text = "Sent by ${user.asUserOrNull()?.tag}" + if (arguments.targetGuild != null) { + val guild = event.kord.getGuildOrNull(arguments.targetGuild!!) + if (guild == null) { + respond { content = "Target guild not found" } + return@ButtonAction + } + + val channel = getLoggingChannelWithPerms(ConfigOptions.UTILITY_LOG, guild) + ?: getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, guild) + ?: getSystemChannelWithPerms(guild) + ?: getFirstUsableChannel(guild) + + if (channel == null) { + respond { content = "Couldn't find an available channel to send a message in" } + return@ButtonAction + } + + channel.createEmbed { + title = modal?.header?.value + description = modal?.body?.value + color = Color(0x7B52AE) + footer { + text = "This message was delivered to this server alone" + } + } + } else { + event.kord.guilds.toList().chunked(15).forEach { chunk -> + for (i in chunk) { + val channel = + getLoggingChannelWithPerms(ConfigOptions.UTILITY_LOG, i) + ?: getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, i) + ?: getSystemChannelWithPerms(i) + ?: getFirstUsableChannel(i) + ?: continue + + channel.createEmbed { + title = modal?.header?.value + description = modal?.body?.value + color = Color(0x7B52AE) + footer { + text = "Sent by ${user.asUserOrNull()?.tag}" + } } } } @@ -89,6 +123,13 @@ class GuildAnnouncements : Extension() { } } + inner class GuildAnnouncementArgs : Arguments() { + val targetGuild by optionalSnowflake { + name = "target-guild" + description = "The guild to send the announcement too" + } + } + inner class GuildAnnouncementModal : ModalForm() { override var title = "Send an announcement" diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/InfoCommands.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/InfoCommands.kt index b4b00331..10586aa6 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/InfoCommands.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/InfoCommands.kt @@ -165,7 +165,7 @@ class InfoCommands : Extension() { */ publicSlashCommand { name = "invite" - description = "Get an invite link for Lily!" + description = "Get an invitation link for Lily!" action { respond { diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/ModUtilities.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/ModUtilities.kt index de1897cd..bd786dc3 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/ModUtilities.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/ModUtilities.kt @@ -55,7 +55,6 @@ import org.hyacinthbots.lilybot.database.collections.NewsChannelPublishingCollec import org.hyacinthbots.lilybot.database.collections.ReminderCollection import org.hyacinthbots.lilybot.database.collections.RoleMenuCollection import org.hyacinthbots.lilybot.database.collections.StatusCollection -import org.hyacinthbots.lilybot.database.collections.SupportConfigCollection import org.hyacinthbots.lilybot.database.collections.TagsCollection import org.hyacinthbots.lilybot.database.collections.ThreadsCollection import org.hyacinthbots.lilybot.database.collections.UtilityConfigCollection @@ -474,7 +473,6 @@ class ModUtilities : Extension() { NewsChannelPublishingCollection().clearAutoPublishingForGuild(guild!!.id) ReminderCollection().removeGuildReminders(guild!!.id) RoleMenuCollection().removeAllRoleMenus(guild!!.id) - SupportConfigCollection().clearConfig(guild!!.id) TagsCollection().clearTags(guild!!.id) ThreadsCollection().removeGuildThreads(guild!!.id) UtilityConfigCollection().clearConfig(guild!!.id) diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/PublicUtilities.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/PublicUtilities.kt index 47e0a8df..35f34efe 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/PublicUtilities.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/PublicUtilities.kt @@ -98,7 +98,6 @@ class PublicUtilities : Extension() { val requesterAsMember = requester?.asMemberOrNull(guild!!.id) val self = this@PublicUtilities.kord.getSelf().asMemberOrNull(guild!!.id) - @Suppress("UnclearPrecedenceOfBinaryExpression") if (requesterAsMember?.getTopRole()?.getPosition() != null && self?.getTopRole()?.getPosition() == null ) { @@ -106,8 +105,8 @@ class PublicUtilities : Extension() { content = "You have a role and Lily does not, so she cannot change your nickname." } return@action - } else if (requesterAsMember?.getTopRole()?.getPosition() ?: 0 > - self?.getTopRole()?.getPosition() ?: 0 + } else if ((requesterAsMember?.getTopRole()?.getPosition() ?: 0) > + (self?.getTopRole()?.getPosition() ?: 0) ) { respond { content = "Your highest role is above Lily's, so she cannot change your nickname." @@ -170,8 +169,8 @@ class PublicUtilities : Extension() { "Please fix Lily's permissions and try again" } return@button - } else if (requesterAsMember?.getTopRole()?.getPosition() ?: 0 > - self?.getTopRole()?.getPosition() ?: 0 + } else if ((requesterAsMember?.getTopRole()?.getPosition() ?: 0) > + (self?.getTopRole()?.getPosition() ?: 0) ) { respond { content = "This user's highest role is above Lily's, " + diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/ThreadControl.kt b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/ThreadControl.kt index 87bbf65b..9557255c 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/ThreadControl.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/extensions/util/ThreadControl.kt @@ -65,13 +65,7 @@ class ThreadControl : Extension() { } action { - val threadChannel = channel.asChannelOfOrNull() - if (threadChannel == null) { - respond { - content = "Are you sure this channel is a thread? If it is, I can't fetch it properly." - return@action - } - } + val threadChannel = getAsThreadChannel() val member = user.asMemberOrNull(guild!!.id) ?: return@action if (!ownsThreadOrModerator(threadChannel!!, member)) return@action @@ -98,13 +92,7 @@ class ThreadControl : Extension() { } action { - val threadChannel = channel.asChannelOfOrNull() - if (threadChannel == null) { - respond { - content = "Are you sure this channel is a thread? If it is, I can't fetch it properly." - return@action - } - } + val threadChannel = getAsThreadChannel() val member = user.asMemberOrNull(guild!!.id) ?: return@action if (!ownsThreadOrModerator(threadChannel!!, member)) return@action @@ -169,13 +157,7 @@ class ThreadControl : Extension() { } action { - val threadChannel = channel.asChannelOfOrNull() - if (threadChannel == null) { - respond { - content = "Are you sure this channel is a thread? If it is, I can't fetch it properly." - return@action - } - } + val threadChannel = getAsThreadChannel() val member = user.asMemberOrNull(guild!!.id) ?: return@action val oldOwnerId = ThreadsCollection().getThread(threadChannel!!.id)?.ownerId ?: threadChannel.ownerId @@ -241,13 +223,7 @@ class ThreadControl : Extension() { } action { - val threadChannel = channel.asChannelOfOrNull() - if (threadChannel == null) { - respond { - content = "Are you sure this channel is a thread? If it is, I can't fetch it properly." - return@action - } - } + val threadChannel = getAsThreadChannel() val member = user.asMemberOrNull(guild!!.id) ?: return@action if (!ownsThreadOrModerator(threadChannel!!, member)) return@action @@ -364,6 +340,26 @@ class ThreadControl : Extension() { } } + /** + * Gets the event channel as a thread channel, and responds appropriately if it cannot be gotten. + * + * @return The channel as a [ThreadChannel] + * + * @author NoComment1105 + * @since 4.8.0 + */ + private suspend inline fun EphemeralSlashCommandContext<*, *>.getAsThreadChannel(): ThreadChannel? { + val threadChannel = channel.asChannelOfOrNull() + if (threadChannel == null) { + respond { + content = "Are you sure this channel is a thread? If it is, I can't fetch it properly." + return null + } + } + + return threadChannel + } + inner class ThreadRenameArgs : Arguments() { /** The new name for the thread. */ val newThreadName by string { diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/utils/_ConfigUtils.kt b/src/main/kotlin/org/hyacinthbots/lilybot/utils/_ConfigUtils.kt index a5df498a..8fea3bd6 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/utils/_ConfigUtils.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/utils/_ConfigUtils.kt @@ -5,7 +5,6 @@ import com.kotlindiscord.kord.extensions.checks.types.CheckContext import dev.kord.common.entity.Snowflake import org.hyacinthbots.lilybot.database.collections.LoggingConfigCollection import org.hyacinthbots.lilybot.database.collections.ModerationConfigCollection -import org.hyacinthbots.lilybot.database.collections.SupportConfigCollection import org.hyacinthbots.lilybot.database.collections.UtilityConfigCollection import org.hyacinthbots.lilybot.extensions.config.ConfigOptions @@ -32,45 +31,6 @@ suspend inline fun CheckContext<*>.requiredConfigs(vararg configOptions: ConfigO // Look at the config options and check the presence of the config in the database. for (option in configOptions) { when (option) { - ConfigOptions.SUPPORT_ENABLED -> { - val supportConfig = SupportConfigCollection().getConfig(guildFor(event)!!.id) - if (supportConfig == null) { - fail("Unable to access support config for this guild! Please inform a member of staff.") - break - } else if (!supportConfig.enabled) { - fail("Support is disabled for this guild!") - break - } else { - pass() - } - } - - ConfigOptions.SUPPORT_CHANNEL -> { - val supportConfig = SupportConfigCollection().getConfig(guildFor(event)!!.id) - if (supportConfig == null) { - fail("Unable to access support config for this guild! Please inform a member of staff.") - break - } else if (supportConfig.channel == null) { - fail("A support channel has not been set for this guild!") - break - } else { - pass() - } - } - - ConfigOptions.SUPPORT_ROLE -> { - val supportConfig = SupportConfigCollection().getConfig(guildFor(event)!!.id) - if (supportConfig == null) { - fail("Unable to access support config for this guild! Please inform a member of staff.") - break - } else if (supportConfig.role == null) { - fail("A support role has not been set for this guild!") - break - } else { - pass() - } - } - ConfigOptions.MODERATION_ENABLED -> { val moderationConfig = ModerationConfigCollection().getConfig(guildFor(event)!!.id) if (moderationConfig == null) { @@ -227,18 +187,6 @@ suspend inline fun CheckContext<*>.requiredConfigs(vararg configOptions: ConfigO */ suspend inline fun configIsUsable(option: ConfigOptions, guildId: Snowflake): Boolean { when (option) { - ConfigOptions.SUPPORT_ENABLED -> return SupportConfigCollection().getConfig(guildId)?.enabled ?: false - - ConfigOptions.SUPPORT_CHANNEL -> { - val supportConfig = SupportConfigCollection().getConfig(guildId) ?: return false - return supportConfig.channel != null - } - - ConfigOptions.SUPPORT_ROLE -> { - val supportConfig = SupportConfigCollection().getConfig(guildId) ?: return false - return supportConfig.role != null - } - ConfigOptions.MODERATION_ENABLED -> return ModerationConfigCollection().getConfig(guildId)?.enabled ?: false ConfigOptions.MODERATOR_ROLE -> { diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/utils/_PermissionUtils.kt b/src/main/kotlin/org/hyacinthbots/lilybot/utils/_PermissionUtils.kt index 7719e668..7d1e9113 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/utils/_PermissionUtils.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/utils/_PermissionUtils.kt @@ -21,7 +21,6 @@ import dev.kord.core.entity.channel.thread.ThreadChannel import kotlinx.coroutines.flow.toList import org.hyacinthbots.lilybot.database.collections.LoggingConfigCollection import org.hyacinthbots.lilybot.database.collections.ModerationConfigCollection -import org.hyacinthbots.lilybot.database.collections.SupportConfigCollection import org.hyacinthbots.lilybot.database.collections.UtilityConfigCollection import org.hyacinthbots.lilybot.extensions.config.ConfigOptions @@ -46,7 +45,6 @@ suspend inline fun getLoggingChannelWithPerms( if (!configIsUsable(channelType, guildId)) return null val channelId = when (channelType) { - ConfigOptions.SUPPORT_CHANNEL -> SupportConfigCollection().getConfig(guildId)?.channel ?: return null ConfigOptions.ACTION_LOG -> ModerationConfigCollection().getConfig(guildId)?.channel ?: return null ConfigOptions.UTILITY_LOG -> UtilityConfigCollection().getConfig(guildId)?.utilityLogChannel ?: return null ConfigOptions.MESSAGE_LOG -> LoggingConfigCollection().getConfig(guildId)?.messageChannel ?: return null @@ -58,7 +56,6 @@ suspend inline fun getLoggingChannelWithPerms( if (!channel.botHasPermissions(Permission.ViewChannel) || !channel.botHasPermissions(Permission.SendMessages)) { if (resetConfig == true) { when (channelType) { - ConfigOptions.SUPPORT_CHANNEL -> SupportConfigCollection().clearConfig(guildId) ConfigOptions.ACTION_LOG -> ModerationConfigCollection().clearConfig(guildId) ConfigOptions.UTILITY_LOG -> UtilityConfigCollection().clearConfig(guildId) ConfigOptions.MESSAGE_LOG -> LoggingConfigCollection().clearConfig(guildId) @@ -88,10 +85,16 @@ suspend inline fun getLoggingChannelWithPerms( * @author tempest15 * @since 3.5.4 */ -suspend inline fun getFirstUsableChannel(inputGuild: GuildBehavior): GuildMessageChannel? = - inputGuild.channels.toList().sorted().firstOrNull { - it.botHasPermissions(Permission.ViewChannel, Permission.SendMessages) - }?.asChannelOfOrNull() +suspend inline fun getFirstUsableChannel(inputGuild: GuildBehavior): GuildMessageChannel? { + var firstUsable: GuildMessageChannel? = null + inputGuild.channels.toList().toSortedSet().forEach { + if (it.botHasPermissions(Permission.ViewChannel) && it.botHasPermissions(Permission.SendMessages)) { + firstUsable = it.asChannelOfOrNull() + return@forEach + } + } + return firstUsable +} /** * Gets a guild's system channel as designated by Discord, or null if said channel is invalid or doesn't exist. @@ -219,7 +222,6 @@ suspend inline fun EphemeralInteractionContext.isBotOrModerator( // Get the users roles into a List of Snowflakes val roles = member.roles.toList().map { it.id } // If the user is a bot, return - @Suppress("UnnecessaryParentheses") if (member.isBot) { respond { content = "You cannot $commandName bot users!" diff --git a/src/main/kotlin/org/hyacinthbots/lilybot/utils/_Utils.kt b/src/main/kotlin/org/hyacinthbots/lilybot/utils/_Utils.kt index 79966f3b..f4d6d476 100644 --- a/src/main/kotlin/org/hyacinthbots/lilybot/utils/_Utils.kt +++ b/src/main/kotlin/org/hyacinthbots/lilybot/utils/_Utils.kt @@ -3,13 +3,30 @@ package org.hyacinthbots.lilybot.utils import com.kotlindiscord.kord.extensions.builders.ExtensibleBotBuilder import com.kotlindiscord.kord.extensions.extensions.Extension import com.kotlindiscord.kord.extensions.utils.loadModule +import com.kotlindiscord.kord.extensions.utils.toDuration +import dev.kord.common.entity.ChannelType +import dev.kord.common.entity.Permission +import dev.kord.common.entity.Snowflake +import dev.kord.core.Kord import dev.kord.core.behavior.GuildBehavior import dev.kord.core.behavior.RoleBehavior +import dev.kord.core.behavior.channel.asChannelOf +import dev.kord.core.behavior.channel.asChannelOfOrNull import dev.kord.core.entity.Message +import dev.kord.core.entity.User +import dev.kord.core.entity.channel.GuildMessageChannel +import dev.kord.core.entity.channel.TextChannel import dev.kord.core.supplier.EntitySupplyStrategy import kotlinx.coroutines.flow.count +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.flow.toSet import kotlinx.coroutines.runBlocking +import kotlinx.datetime.Clock import kotlinx.datetime.DateTimePeriod +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime import mu.KotlinLogging import org.hyacinthbots.discordmoderationactions.enums.DmResult import org.hyacinthbots.lilybot.database.Database @@ -24,7 +41,6 @@ import org.hyacinthbots.lilybot.database.collections.NewsChannelPublishingCollec import org.hyacinthbots.lilybot.database.collections.ReminderCollection import org.hyacinthbots.lilybot.database.collections.RoleMenuCollection import org.hyacinthbots.lilybot.database.collections.StatusCollection -import org.hyacinthbots.lilybot.database.collections.SupportConfigCollection import org.hyacinthbots.lilybot.database.collections.TagsCollection import org.hyacinthbots.lilybot.database.collections.ThreadsCollection import org.hyacinthbots.lilybot.database.collections.UptimeCollection @@ -155,7 +171,7 @@ fun DateTimePeriod?.interval(): String? { /** * Get this message's contents, trimmed to the [desiredLength] of characters. * If the message exceeds that length, it will be truncated and an ellipsis appended. - * If the message is smaller than the [desiredLength], the content length is used and an elipsis appended + * If the message is smaller than the [desiredLength], the content length is used and an ellipsis appended * * @param desiredLength The desired length to limit the string too * @author NoComment1105 @@ -201,6 +217,65 @@ suspend inline fun Extension.updateDefaultPresence() { } } +/** + * A quick function to generate the contents of the bulk delete file. + * + * @param messages The set of message being deleted + * @return A string of the file content + * + * @author NoComment1105 + * @since 4.7.0 + */ +fun generateBulkDeleteFile(messages: Set): String = + "# Messages\n\n**Total:** ${messages.size}\n\n" + + messages.reversed().joinToString("\n") { // Reversed for chronology + "* [${ + it.timestamp.toLocalDateTime(TimeZone.UTC).toString().replace("T", " @ ") + } UTC] **${it.author?.username}** (${it.author?.id}) » ${it.content}" + } + +/** + * Gets the messages from the guild channels that will be deleted. + * **This function is very, very intensive** It should be used in minimal quantities and future efforts should be made + * to optimise it further. Yes, it was worse that its current state. + * + * @param guild The guild to get the messages from + * @param user The user that send the messages + * @param messages The number of days worth of messages to delete + * @param kord The kord instance + * @return A [MutableSet] of [Message]s that are being deleted + * + * @author NoComment1105 + * @since 4.7.0 + */ +suspend inline fun getMessagesForBanDelete( + guild: GuildBehavior?, + user: User, + messages: Int?, + kord: Kord +): MutableSet { + if (messages == null || messages == 0) return mutableSetOf() + + val checkTime = Clock.System.now().minus(DateTimePeriod(days = messages).toDuration(TimeZone.UTC)) + + val fullSet = mutableSetOf() + + val shortList = guild!!.channels.filter { + it.type == ChannelType.GuildText && + it.asChannelOf().getEffectivePermissions(kord.selfId).contains(Permission.ViewChannel) + }.map { it.asChannelOfOrNull() }.toList() + + shortList.forEach { + val messagesSet = it?.getMessagesBefore(Snowflake.max)?.filter { message -> + message.author == user && message.timestamp.epochSeconds > checkTime.epochSeconds && message.content.isNotEmpty() + }?.toSet()?.reversed() // Glorious chronology + + if (messagesSet != null) fullSet.addAll(messagesSet) + } + + return fullSet +} + /** * This function loads the database and checks if it is up-to-date. If it isn't, it will update the database via * migrations. @@ -228,7 +303,6 @@ suspend inline fun ExtensibleBotBuilder.database(migrate: Boolean) { single { ReminderCollection() } bind ReminderCollection::class single { RoleMenuCollection() } bind RoleMenuCollection::class single { StatusCollection() } bind StatusCollection::class - single { SupportConfigCollection() } bind SupportConfigCollection::class single { TagsCollection() } bind TagsCollection::class single { ThreadsCollection() } bind ThreadsCollection::class single { UptimeCollection() } bind UptimeCollection::class