Skip to content

Commit

Permalink
Switch to official mongo driver (#351)
Browse files Browse the repository at this point in the history
* Switch to the Official MongoDB Driver. Needs testing

* Some fixes, still broken though

* Switch to kordex mongodb data adapter

* Update kordex to fix mongo bug

* Fix meta breaking the whole thing

* Use a constant to determine the collection name

* Revert "Use a constant to determine the collection name"

This reverts commit f61306c.

* Use a constant to determine the collection name but do it better

* Simplify filtering in cleanup

* Update the README.md
  • Loading branch information
NoComment1105 committed Sep 8, 2023
1 parent ee7a972 commit 8557d4f
Show file tree
Hide file tree
Showing 60 changed files with 389 additions and 368 deletions.
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 = "1.3.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.9"
github-api = "1.315"
kmongo = "4.9.0"
mongo-driver = "4.10.1"
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)
15 changes: 12 additions & 3 deletions src/main/kotlin/org/hyacinthbots/lilybot/database/Database.kt
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
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")

/** The database for storing per guild configuration data. */
val configDatabase get() = client.getDatabase("LilyBotConfig")

val tempKordExDatabase get() = client.getDatabase("kordex-data")

/**
* Runs the migrations for both databases.
*
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

0 comments on commit 8557d4f

Please sign in to comment.