Skip to content

Commit

Permalink
Update KordEx and stop using DMA
Browse files Browse the repository at this point in the history
  • Loading branch information
NoComment1105 committed Mar 19, 2024
1 parent 36391fd commit ccdfcd9
Show file tree
Hide file tree
Showing 9 changed files with 501 additions and 537 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,14 @@ If you're looking to set up a development environment for Lily, try our [develop
Lily makes use of the following tools and frameworks.
* [Kord](https://github.com/kordlib/kord), the Kotlin API for Discord.
* [KordEx](https://github.com/Kord-Extensions/kord-extensions), an integrated commands and extensions framework for Kord.
* KordEx's [Phishing](https://github.com/Kord-Extensions/kord-extensions/tree/develop/extra-modules/extra-phishing), [PluralKit](https://github.com/Kord-Extensions/kord-extensions/tree/develop/extra-modules/extra-pluralkit) and [Unsafe](https://github.com/Kord-Extensions/kord-extensions/tree/develop/modules/unsafe) modules.
* KordEx's [Phishing](https://github.com/Kord-Extensions/kord-extensions/tree/root/extra-modules/extra-phishing), [PluralKit](https://github.com/Kord-Extensions/kord-extensions/tree/root/extra-modules/extra-pluralkit), [Unsafe](https://github.com/Kord-Extensions/kord-extensions/tree/develop/modules/unsafe) and [Welcome Channel](https://github.com/Kord-Extensions/kord-extensions/tree/root/extra-modules/extra-welcome) modules.
* [MongoDB](https://www.mongodb.com/) and [KMongo](https://litote.org/kmongo/)
to manage the database.
* [Logback](https://github.com/qos-ch/logback), a library that makes logging prettier
* [Kotlin Logging](https://github.com/MicroUtils/kotlin-logging), a lightweight logging that wraps SLF4J with kotlin extensions
* [Github-API](https://github.com/hub4j/github-api), the API utilized by the GitHub commands
* [Shadow Gradle Plugin](https://github.com/johnrengelman/shadow), a tool which allows us to make a big fatjar containing all dependencies
* [detekt](https://detekt.dev/index.html), a static code analysis tool for the Kotlin programming language. This helps us keep our code clean and nicely formatted.
* [Cozy's Welcome channel module](https://github.com/QuiltMC/cozy-discord/tree/root/module-welcome) made by our friends at [QuiltMC](https://quiltmc.org/)
* [Discord Moderator Actions](https://github.com/NoComment1105/discord-moderation-actions), a library for making moderation commands easier, through the use of DSL.
* [Doc-generator](https://github.com/HyacinthBots/doc-generator), our in-house documentation generator for KordEx bots

#### Contributing
Expand Down
1 change: 0 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ dependencies {
// KMongo
implementation(libs.kmongo)

implementation(libs.dma)
implementation(libs.docgenerator)
}

Expand Down
4 changes: 1 addition & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ grgit = "5.2.2"
blossom = "2.1.0"

# Libraries
kord-extensions = "1.8.0-20240228.171743-12"
kord-extensions = "1.8.0-20240319.115836-21"
logging = "6.0.3"
logback = "1.5.0"
github-api = "1.318"
kmongo = "4.11.0"
dma = "0.2.3-SNAPSHOT"
docgenerator = "0.1.5-SNAPSHOT"

[libraries]
Expand All @@ -28,7 +27,6 @@ logging = { module = "io.github.oshai:kotlin-logging", version.ref = "logging" }
github-api = { module = "org.kohsuke:github-api", version.ref = "github-api" }
kmongo = { module = "org.litote.kmongo:kmongo-coroutine-serialization", version.ref = "kmongo" }
detekt = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt"}
dma = { module = "org.hyacinthbots:discord-moderation-actions", version.ref = "dma"}
docgenerator = { module = "org.hyacinthbots:doc-generator", version.ref = "docgenerator" }

[plugins]
Expand Down
9 changes: 7 additions & 2 deletions src/main/kotlin/org/hyacinthbots/lilybot/LilyBot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.hyacinthbots.lilybot.extensions.events.MemberLogging
import org.hyacinthbots.lilybot.extensions.events.MessageDelete
import org.hyacinthbots.lilybot.extensions.events.MessageEdit
import org.hyacinthbots.lilybot.extensions.events.ModThreadInviting
import org.hyacinthbots.lilybot.extensions.moderation.ClearCommands
import org.hyacinthbots.lilybot.extensions.moderation.LockingCommands
import org.hyacinthbots.lilybot.extensions.moderation.ModerationCommands
import org.hyacinthbots.lilybot.extensions.moderation.Report
Expand All @@ -51,6 +52,7 @@ import org.kohsuke.github.GitHubBuilder
import java.io.IOException
import kotlin.io.path.Path
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

lateinit var github: GitHub
private val gitHubLogger = KotlinLogging.logger("GitHub Logger")
Expand All @@ -75,6 +77,7 @@ suspend fun main() {
// Add the extensions to the bot
extensions {
add(::AutoThreading)
add(::ClearCommands)
add(::Config)
add(::GalleryChannel)
add(::Github)
Expand Down Expand Up @@ -120,13 +123,15 @@ suspend fun main() {
control of their account
*/
extPhishing {
appName = "Lily Bot"
detectionAction = DetectionAction.Kick
logChannelName = "anti-phishing-logs"
requiredCommandPermission = null
}

extPluralKit()
extPluralKit {
defaultLimit(4, 1.seconds)
domainLimit("api.pluralkit.me", 2, 1.seconds)
}

sentry {
enableIfDSN(SENTRY_DSN) // Use the nullable sentry function to allow the bot to be used without a DSN
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
package org.hyacinthbots.lilybot.extensions.moderation

import com.kotlindiscord.kord.extensions.DISCORD_BLACK
import com.kotlindiscord.kord.extensions.checks.anyGuild
import com.kotlindiscord.kord.extensions.checks.hasPermission
import com.kotlindiscord.kord.extensions.commands.Arguments
import com.kotlindiscord.kord.extensions.commands.application.slash.EphemeralSlashCommandContext
import com.kotlindiscord.kord.extensions.commands.application.slash.ephemeralSubCommand
import com.kotlindiscord.kord.extensions.commands.converters.impl.int
import com.kotlindiscord.kord.extensions.commands.converters.impl.optionalInt
import com.kotlindiscord.kord.extensions.commands.converters.impl.optionalUser
import com.kotlindiscord.kord.extensions.commands.converters.impl.snowflake
import com.kotlindiscord.kord.extensions.extensions.Extension
import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand
import dev.kord.common.entity.Permission
import dev.kord.common.entity.Permissions
import dev.kord.common.entity.Snowflake
import dev.kord.core.behavior.channel.asChannelOfOrNull
import dev.kord.core.behavior.channel.createEmbed
import dev.kord.core.entity.User
import dev.kord.core.entity.channel.GuildMessageChannel
import dev.kord.core.supplier.EntitySupplyStrategy
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toSet
import org.hyacinthbots.lilybot.database.collections.ModerationConfigCollection
import org.hyacinthbots.lilybot.extensions.config.ConfigOptions
import org.hyacinthbots.lilybot.utils.botHasChannelPerms
import org.hyacinthbots.lilybot.utils.getLoggingChannelWithPerms
import org.hyacinthbots.lilybot.utils.modCommandChecks
import org.hyacinthbots.lilybot.utils.requiredConfigs
import kotlin.math.min

class ClearCommands : Extension() {
override val name = "clear"

override suspend fun setup() {
ephemeralSlashCommand {
name = "clear"
description = "Parent command for clear commands"

ephemeralSubCommand(ClearCommandArgs::Count) {
name = "count"
description = "Clear a specific count of messages"

requirePermission(Permission.ManageMessages)

check {
modCommandChecks(Permission.ManageMessages)
requireBotPermissions(Permission.ManageMessages)
botHasChannelPerms(Permissions(Permission.ManageMessages))
}

action {
clearMessages(arguments.count, null, null, arguments.author)
}
}

ephemeralSubCommand(ClearCommandArgs::Before) {
name = "before"
description = "Clear messages before a given message ID"

requirePermission(Permission.ManageMessages)

check {
modCommandChecks(Permission.ManageMessages)
requireBotPermissions(Permission.ManageMessages)
botHasChannelPerms(Permissions(Permission.ManageMessages))
}

action {
clearMessages(arguments.count, Snowflake(arguments.before.value + 1u), null, arguments.author)
}
}

ephemeralSubCommand(ClearCommandArgs::After) {
name = "after"
description = "Clear messages before a given message ID"

requirePermission(Permission.ManageMessages)

check {
modCommandChecks(Permission.ManageMessages)
requireBotPermissions(Permission.ManageMessages)
botHasChannelPerms(Permissions(Permission.ManageMessages))
}

action {
clearMessages(arguments.count, null, Snowflake(arguments.after.value - 1u), arguments.author)
}
}

ephemeralSubCommand(ClearCommandArgs::Between) {
name = "between"
description = "Clear messages between 2 message IDs"

requirePermission(Permission.ManageMessages)

check {
anyGuild()
requiredConfigs(ConfigOptions.MODERATION_ENABLED)
hasPermission(Permission.ManageMessages)
requireBotPermissions(Permission.ManageMessages)
botHasChannelPerms(Permissions(Permission.ManageMessages))
}

action {
clearMessages(
null,
Snowflake(arguments.before.value - 1u),
Snowflake(arguments.after.value + 1u),
arguments.author
)
}
}
}
}

/**
* An object containing the arguments for clear commands.
*
* @since 4.8.6
*/
@Suppress("MemberNameEqualsClassName") // Cope
internal object ClearCommandArgs {
/** Clear a specific count of messages. */
internal class Count : Arguments() {
/** The number of messages the user wants to remove. */
val count by int {
name = "messages"
description = "Number of messages to delete"
}

/** The author of the messages that need clearing. */
val author by optionalUser {
name = "author"
description = "The author of the messages to clear"
}
}

/** Clear messages after a specific one. */
internal class After : Arguments() {
/** The ID of the message to start clearing from. */
val after by snowflake {
name = "after"
description = "The ID of the message to clear after"
}

/** The number of messages the user wants to remove. */
val count by optionalInt {
name = "message-count"
description = "The number of messages to clear"
}

/** The author of the messages that need clearing. */
val author by optionalUser {
name = "author"
description = "The author of the messages to clear"
}
}

/** Clear messages before a specific one. */
internal class Before : Arguments() {
/** The ID of the message to start clearing before. */
val before by snowflake {
name = "before"
description = "The ID of the message to clear before"
}

/** The number of messages the user wants to remove. */
val count by optionalInt {
name = "message-count"
description = "The number of messages to clear"
}

/** The author of the messages that need clearing. */
val author by optionalUser {
name = "author"
description = "The author of the messages to clear"
}
}

/** Clear messages between 2 specific ones. */
internal class Between : Arguments() {
/** The ID of the message to start clearing from. */
val after by snowflake {
name = "after"
description = "The ID of the message to clear after"
}

/** The ID of the message to start clearing before. */
val before by snowflake {
name = "before"
description = "The ID of the message to clear before"
}

/** The author of the messages that need clearing. */
val author by optionalUser {
name = "author"
description = "The author of the messages to clear"
}
}
}
}

/**
* A function to use clear messages based on the count, before and after, as well as a user.
*
* @param count The number of messages to clear, or null
* @param before The ID of the message to clear messages before
* @param after The ID of the message to clear messages after
* @param author The author of the messages that should be cleared
* @author NoComment1105
* @since 4.8.6
*/
private suspend fun EphemeralSlashCommandContext<*, *>.clearMessages(
count: Int?,
before: Snowflake?,
after: Snowflake?,
author: User?
) {
val config = ModerationConfigCollection().getConfig(guild!!.id)!!
val textChannel = channel.asChannelOfOrNull<GuildMessageChannel>()

if (textChannel == null) {
respond {
content = "Could not get the channel to clear messages from."
}
return
}

if ((before != null && after != null) && (before < after)) {
respond {
content = "Before cannot be more recent than after!"
}
return
}

// Get the specified amount of messages into an array list of Snowflakes and delete them
// Send help
val messageFlow = if (before == null && after == null) {
channel.withStrategy(EntitySupplyStrategy.rest)
.getMessagesBefore(Snowflake.max, count?.let { min(it, 100) })
} else if (after != null && before == null) {
channel.withStrategy(EntitySupplyStrategy.rest).getMessagesAfter(after, count?.let { min(it, 100) })
} else if (after == null && before != null) {
channel.withStrategy(EntitySupplyStrategy.rest).getMessagesBefore(before, count?.let { min(it, 100) })
} else if (after != null && before != null) {
channel.withStrategy(EntitySupplyStrategy.rest).getMessagesBefore(before, count?.let { min(it, 100) })
.filter { it.id > after }
} else {
flowOf()
}

val messages = if (author == null) {
messageFlow.map { it.id }.toSet()
} else {
messageFlow.filter { it.author == author }.map { it.id }.toSet()
}

textChannel.bulkDelete(messages)

respond {
content = "Messages cleared."
}

if (config.publicLogging != null && config.publicLogging == true) {
channel.createEmbed {
title = "$count messages have been cleared."
color = DISCORD_BLACK
}
}

val actionLog =
getLoggingChannelWithPerms(ConfigOptions.ACTION_LOG, this.getGuild()!!) ?: return
actionLog.createEmbed {
title = "${count ?: messages.size} messages have been cleared."
description = "Action occurred in ${textChannel.mention}"
footer {
text = user.asUserOrNull()?.username ?: "Unable to get username"
icon = user.asUserOrNull()?.avatar?.cdnUrl?.toUrl()
}
color = DISCORD_BLACK
}
}

0 comments on commit ccdfcd9

Please sign in to comment.