Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #356

Merged
merged 3 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ 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.
* [MongoDB](https://www.mongodb.com/) and [KMongo](https://litote.org/kmongo/)
* [MongoDB](https://www.mongodb.com/)'s [Kotlin Driver](https://www.mongodb.com/docs/drivers/kotlin/)
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
Expand Down
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ dependencies {
implementation(libs.kord.extensions.phishing)
implementation(libs.kord.extensions.pluralkit)
implementation(libs.kord.extensions.unsafe)
implementation(libs.kord.extensions.mongodb)

implementation(libs.kotlin.stdlib)

Expand All @@ -64,7 +65,7 @@ dependencies {
implementation(libs.github.api)

// KMongo
implementation(libs.kmongo)
implementation(libs.mongo.driver)

// Cozy's welcome module
implementation(libs.cozy.welcome)
Expand Down
8 changes: 5 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ grgit = "5.2.0"
blossom = "2.0.1"

# Libraries
kord-extensions = "1.5.9-20230809.104126-1"
#kord-extensions = "1.5.9-20230820.204324-8"
kord-extensions = "1.5.9-SNAPSHOT"
logging = "5.1.0"
logback = "1.4.11"
github-api = "1.316"
kmongo = "4.10.0"
mongo-driver = "4.10.2"
cozy-welcome = "1.0.1-SNAPSHOT"
dma = "v0.2.1"
docgenerator = "0.1.2-SNAPSHOT"
Expand All @@ -22,11 +23,12 @@ kord-extensions-core = { module = "com.kotlindiscord.kord.extensions:kord-extens
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"}
kord-extensions-mongodb = { module = "com.kotlindiscord.kord.extensions:adapter-mongodb", version.ref = "kord-extensions" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib" }
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
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" }
mongo-driver = { module = "org.mongodb:mongodb-driver-kotlin-coroutine", version.ref = "mongo-driver" }
detekt = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt"}
cozy-welcome = {module = "org.quiltmc.community:module-welcome", version.ref = "cozy-welcome"}
dma = { module = "org.hyacinthbots:discord-moderation-actions", version.ref = "dma"}
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/org/hyacinthbots/lilybot/LilyBot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package org.hyacinthbots.lilybot

import com.kotlindiscord.kord.extensions.ExtensibleBot
import com.kotlindiscord.kord.extensions.adapters.mongodb.mongoDB
import com.kotlindiscord.kord.extensions.checks.hasPermission
import com.kotlindiscord.kord.extensions.modules.extra.phishing.DetectionAction
import com.kotlindiscord.kord.extensions.modules.extra.phishing.extPhishing
Expand All @@ -15,7 +16,6 @@ import org.hyacinthbots.docgenerator.docsGenerator
import org.hyacinthbots.docgenerator.enums.CommandTypes
import org.hyacinthbots.docgenerator.enums.SupportedFileFormat
import org.hyacinthbots.lilybot.database.collections.WelcomeChannelCollection
import org.hyacinthbots.lilybot.database.storage.MongoDBDataAdapter
import org.hyacinthbots.lilybot.extensions.config.Config
import org.hyacinthbots.lilybot.extensions.config.ConfigOptions
import org.hyacinthbots.lilybot.extensions.config.GuildLogging
Expand Down Expand Up @@ -60,7 +60,7 @@ val docFile = Path("./docs/commands.md")
suspend fun main() {
val bot = ExtensibleBot(BOT_TOKEN) {
database(true)
dataAdapter(::MongoDBDataAdapter)
mongoDB()

members {
lockMemberRequests = true // Collect members one at a time to avoid hitting rate limits
Expand Down
9 changes: 5 additions & 4 deletions src/main/kotlin/org/hyacinthbots/lilybot/database/Cleanups.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.hyacinthbots.lilybot.database

import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent
import com.mongodb.client.model.Filters.eq
import dev.kord.core.Kord
import dev.kord.core.behavior.getChannelOfOrNull
import dev.kord.core.entity.channel.thread.ThreadChannel
import dev.kord.rest.request.KtorRequestException
import kotlinx.coroutines.flow.toList
import kotlinx.datetime.Clock
import mu.KotlinLogging
import org.hyacinthbots.lilybot.database.Cleanups.cleanupGuildData
Expand All @@ -24,7 +26,6 @@ import org.hyacinthbots.lilybot.database.collections.WelcomeChannelCollection
import org.hyacinthbots.lilybot.database.entities.GuildLeaveTimeData
import org.hyacinthbots.lilybot.database.entities.ThreadData
import org.koin.core.component.inject
import org.litote.kmongo.eq

/**
* This object contains the Database clean up functions, for removing old data from the database that Lily no longer
Expand All @@ -38,10 +39,10 @@ object Cleanups : KordExKoinComponent {
private val db: Database by inject()

@PublishedApi
internal val guildLeaveTimeCollection = db.mainDatabase.getCollection<GuildLeaveTimeData>()
internal val guildLeaveTimeCollection = db.mainDatabase.getCollection<GuildLeaveTimeData>(GuildLeaveTimeData.name)

@PublishedApi
internal val threadDataCollection = db.mainDatabase.getCollection<ThreadData>()
internal val threadDataCollection = db.mainDatabase.getCollection<ThreadData>(ThreadData.name)

@PublishedApi
internal val cleanupsLogger = KotlinLogging.logger("Database Cleanups")
Expand Down Expand Up @@ -75,7 +76,7 @@ object Cleanups : KordExKoinComponent {
UtilityConfigCollection().clearConfig(it.guildId)
WarnCollection().clearWarns(it.guildId)
WelcomeChannelCollection().removeWelcomeChannelsForGuild(it.guildId, kord)
guildLeaveTimeCollection.deleteOne(GuildLeaveTimeData::guildId eq it.guildId)
guildLeaveTimeCollection.deleteOne(eq(GuildLeaveTimeData::guildId.name, it.guildId))
deletedGuildData += 1 // Increment the counter for logging
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/main/kotlin/org/hyacinthbots/lilybot/database/Collection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.hyacinthbots.lilybot.database

/**
* This class stores the name of a collection, so it can be referenced easily, avoiding string duplication
*
* An example of how it should be used can be found below. The name property used in the get collection line comes from
* this class
* ```kt
* class MagicCollection : KordExKoinComponent {
* private val db: Database by inject()
*
* val collection = db.mainDatabase.getCollection<MagicCollectionData>(name)
*
* suspend fun get(): List<MagicCollectionData> = collection.find().toList()
*
* companion object : Collection("magicCollection")
* }
* ```
*
* @property name The name of the collection
*/
abstract class Collection(val name: String)
13 changes: 10 additions & 3 deletions src/main/kotlin/org/hyacinthbots/lilybot/database/Database.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package org.hyacinthbots.lilybot.database

import com.kotlindiscord.kord.extensions.adapters.mongodb.kordExCodecRegistry
import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import com.mongodb.kotlin.client.coroutine.MongoClient
import org.bson.UuidRepresentation
import org.bson.codecs.configuration.CodecRegistries
import org.hyacinthbots.lilybot.database.migrations.Migrator
import org.hyacinthbots.lilybot.utils.MONGO_URI
import org.litote.kmongo.coroutine.coroutine
import org.litote.kmongo.reactivestreams.KMongo

class Database {
private val codecRegistries = CodecRegistries.fromRegistries(
kordExCodecRegistry,
MongoClientSettings.getDefaultCodecRegistry()
)

// Connect to the database using the provided connection URL
private val settings = MongoClientSettings
.builder()
.uuidRepresentation(UuidRepresentation.STANDARD)
.applyConnectionString(ConnectionString(MONGO_URI))
.codecRegistry(codecRegistries)
.build()

private val client = KMongo.createClient(settings).coroutine
private val client = MongoClient.create(settings)

/** The main database for storing data. */
val mainDatabase get() = client.getDatabase("LilyBot")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.hyacinthbots.lilybot.database

import com.mongodb.client.model.Filters
import com.mongodb.client.model.Filters.and
import com.mongodb.kotlin.client.coroutine.FindFlow
import com.mongodb.kotlin.client.coroutine.MongoCollection
import kotlinx.coroutines.flow.firstOrNull
import org.bson.conversions.Bson

private fun <T : Any> MongoCollection<T>.find(vararg filters: Bson?): FindFlow<T> = find(and(*filters))

suspend fun <T : Any> MongoCollection<T>.findOne(filter: Bson): T? = find(filter).firstOrNull()

suspend fun <T : Any> MongoCollection<T>.findOne(vararg filters: Bson?): T? = find(*filters).firstOrNull()

suspend fun <T : Any> MongoCollection<T>.deleteOne() = deleteOne(Filters.empty())

// TODO Make more cool useful functions
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.hyacinthbots.lilybot.database.collections

import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent
import com.mongodb.client.model.Filters.eq
import dev.kord.common.entity.Snowflake
import kotlinx.coroutines.flow.toList
import org.hyacinthbots.lilybot.database.Database
import org.hyacinthbots.lilybot.database.entities.AutoThreadingData
import org.hyacinthbots.lilybot.database.findOne
import org.koin.core.component.inject
import org.litote.kmongo.eq

/**
* This class contains the functions for interacting with the [AutoThreading Database][AutoThreadingData]. This
Expand All @@ -22,7 +24,7 @@ class AutoThreadingCollection : KordExKoinComponent {
private val db: Database by inject()

@PublishedApi
internal val collection = db.mainDatabase.getCollection<AutoThreadingData>()
internal val collection = db.mainDatabase.getCollection<AutoThreadingData>(AutoThreadingData.name)

/**
* Gets all auto threads for a given [inputGuildId].
Expand All @@ -33,7 +35,7 @@ class AutoThreadingCollection : KordExKoinComponent {
* @since 4.6.0
*/
suspend inline fun getAllAutoThreads(inputGuildId: Snowflake): List<AutoThreadingData> =
collection.find(AutoThreadingData::guildId eq inputGuildId).toList()
collection.find(eq(AutoThreadingData::guildId.name, inputGuildId)).toList()

/**
* Gets a single auto thread based off the channel ID.
Expand All @@ -44,7 +46,7 @@ class AutoThreadingCollection : KordExKoinComponent {
* @since 4.6.0
*/
suspend inline fun getSingleAutoThread(inputChannelId: Snowflake): AutoThreadingData? =
collection.findOne(AutoThreadingData::channelId eq inputChannelId)
collection.findOne(eq(AutoThreadingData::channelId.name, inputChannelId))

/**
* Sets a new auto thread.
Expand All @@ -54,7 +56,7 @@ class AutoThreadingCollection : KordExKoinComponent {
* @since 4.6.0
*/
suspend inline fun setAutoThread(inputAutoThreadData: AutoThreadingData) {
collection.deleteOne(AutoThreadingData::channelId eq inputAutoThreadData.channelId)
collection.deleteOne(eq(AutoThreadingData::channelId.name, inputAutoThreadData.channelId))
collection.insertOne(inputAutoThreadData)
}

Expand All @@ -66,7 +68,7 @@ class AutoThreadingCollection : KordExKoinComponent {
* @since 4.6.0
*/
suspend inline fun deleteAutoThread(inputChannelId: Snowflake) =
collection.deleteOne(AutoThreadingData::channelId eq inputChannelId)
collection.deleteOne(eq(AutoThreadingData::channelId.name, inputChannelId))

/**
* Deletes auto threads for a given guild.
Expand All @@ -76,5 +78,5 @@ class AutoThreadingCollection : KordExKoinComponent {
* @since 4.6.0
*/
suspend inline fun deleteGuildAutoThreads(inputGuildId: Snowflake) =
collection.deleteMany(AutoThreadingData::guildId eq inputGuildId)
collection.deleteMany(eq(AutoThreadingData::guildId.name, inputGuildId))
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.hyacinthbots.lilybot.database.collections

import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent
import com.mongodb.client.model.Filters.eq
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.UtilityConfigData
import org.hyacinthbots.lilybot.database.findOne
import org.koin.core.component.inject
import org.litote.kmongo.eq

/**
* This class contains the functions for interacting with the [Logging Config Database][LoggingConfigData]. This class
Expand All @@ -22,7 +23,7 @@ class LoggingConfigCollection : KordExKoinComponent {
private val configDb: Database by inject()

@PublishedApi
internal val collection = configDb.configDatabase.getCollection<LoggingConfigData>()
internal val collection = configDb.configDatabase.getCollection<LoggingConfigData>(LoggingConfigData.name)

/**
* Gets the logging config for the given guild using the [guildId][inputGuildId].
Expand All @@ -33,7 +34,7 @@ class LoggingConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun getConfig(inputGuildId: Snowflake): LoggingConfigData? =
collection.findOne(LoggingConfigData::guildId eq inputGuildId)
collection.findOne(eq(LoggingConfigData::guildId.name, inputGuildId))

/**
* Adds the given [loggingConfig] to the database.
Expand All @@ -43,7 +44,7 @@ class LoggingConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun setConfig(loggingConfig: LoggingConfigData) {
collection.deleteOne(LoggingConfigData::guildId eq loggingConfig.guildId)
collection.deleteOne(eq(LoggingConfigData::guildId.name, loggingConfig.guildId))
collection.insertOne(loggingConfig)
}

Expand All @@ -55,7 +56,7 @@ class LoggingConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun clearConfig(inputGuildId: Snowflake) =
collection.deleteOne(LoggingConfigData::guildId eq inputGuildId)
collection.deleteOne(eq(LoggingConfigData::guildId.name, inputGuildId))
}

/**
Expand All @@ -71,7 +72,7 @@ class ModerationConfigCollection : KordExKoinComponent {
private val configDb: Database by inject()

@PublishedApi
internal val collection = configDb.configDatabase.getCollection<ModerationConfigData>()
internal val collection = configDb.configDatabase.getCollection<ModerationConfigData>(ModerationConfigData.name)

/**
* Gets the Moderation config for the given guild using the [guildId][inputGuildId].
Expand All @@ -82,7 +83,7 @@ class ModerationConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun getConfig(inputGuildId: Snowflake): ModerationConfigData? =
collection.findOne(ModerationConfigData::guildId eq inputGuildId)
collection.findOne(eq(ModerationConfigData::guildId.name, inputGuildId))

/**
* Adds the given [moderationConfig] to the database.
Expand All @@ -92,7 +93,7 @@ class ModerationConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun setConfig(moderationConfig: ModerationConfigData) {
collection.deleteOne(ModerationConfigData::guildId eq moderationConfig.guildId)
collection.deleteOne(eq(ModerationConfigData::guildId.name, moderationConfig.guildId))
collection.insertOne(moderationConfig)
}

Expand All @@ -104,7 +105,7 @@ class ModerationConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun clearConfig(inputGuildId: Snowflake) =
collection.deleteOne(ModerationConfigData::guildId eq inputGuildId)
collection.deleteOne(eq(ModerationConfigData::guildId.name, inputGuildId))
}

/**
Expand All @@ -120,7 +121,7 @@ class UtilityConfigCollection : KordExKoinComponent {
private val configDb: Database by inject()

@PublishedApi
internal val collection = configDb.configDatabase.getCollection<UtilityConfigData>()
internal val collection = configDb.configDatabase.getCollection<UtilityConfigData>(UtilityConfigData.name)

/**
* Gets the Utility config for the given guild using the [guildId][inputGuildId].
Expand All @@ -131,7 +132,7 @@ class UtilityConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun getConfig(inputGuildId: Snowflake): UtilityConfigData? =
collection.findOne(UtilityConfigData::guildId eq inputGuildId)
collection.findOne(eq(UtilityConfigData::guildId.name, inputGuildId))

/**
* Adds the given [utilityConfig] to the database.
Expand All @@ -141,7 +142,7 @@ class UtilityConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun setConfig(utilityConfig: UtilityConfigData) {
collection.deleteOne(UtilityConfigData::guildId eq utilityConfig.guildId)
collection.deleteOne(eq(UtilityConfigData::guildId.name, utilityConfig.guildId))
collection.insertOne(utilityConfig)
}

Expand All @@ -153,5 +154,5 @@ class UtilityConfigCollection : KordExKoinComponent {
* @since 4.0.0
*/
suspend inline fun clearConfig(inputGuildId: Snowflake) =
collection.deleteOne(UtilityConfigData::guildId eq inputGuildId)
collection.deleteOne(eq(UtilityConfigData::guildId.name, inputGuildId))
}
Loading