1
0

add first database revision

This commit is contained in:
Jannik Reimers 2024-11-05 22:15:46 +01:00
parent 69a08ce6d4
commit 65e4a3d77c
Signed by: jansel
GPG Key ID: 39C62D7D5233CFD0
10 changed files with 192 additions and 47 deletions

View File

@ -20,6 +20,7 @@ dependencies {
implementation(libs.kx.ser) implementation(libs.kx.ser)
implementation(libs.kx.coroutines) implementation(libs.kx.coroutines)
implementation(libs.twitch4j) implementation(libs.twitch4j)
implementation(libs.kmongo)
// Logging dependencies // Logging dependencies
implementation(libs.groovy) implementation(libs.groovy)
@ -32,6 +33,8 @@ dependencies {
kordEx { kordEx {
kordExVersion = "2.3.1-SNAPSHOT" kordExVersion = "2.3.1-SNAPSHOT"
jvmTarget = 21
bot { bot {
// See https://docs.kordex.dev/data-collection.html // See https://docs.kordex.dev/data-collection.html
dataCollection(DataCollection.Standard) dataCollection(DataCollection.Standard)
@ -79,3 +82,8 @@ docker {
) )
} }
} }
tasks.wrapper {
gradleVersion = "8.10.2"
distributionType = Wrapper.DistributionType.BIN
}

View File

@ -10,6 +10,7 @@ logging = "7.0.0"
twitch4j = "1.22.0" twitch4j = "1.22.0"
events4j = "0.12.2" events4j = "0.12.2"
kx-coroutines = "1.9.0" kx-coroutines = "1.9.0"
kmongo = "4.9.0"
[libraries] [libraries]
groovy = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" } groovy = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
@ -22,3 +23,4 @@ logback-groovy = { module = "io.github.virtualdogbert:logback-groovy-config", ve
logging = { module = "io.github.oshai:kotlin-logging", version.ref = "logging" } logging = { module = "io.github.oshai:kotlin-logging", version.ref = "logging" }
twitch4j = { module = "com.github.twitch4j:twitch4j", version.ref = "twitch4j" } twitch4j = { module = "com.github.twitch4j:twitch4j", version.ref = "twitch4j" }
events4j = { module = "com.github.philippheuer.events4j:events4j-handler-simple", version.ref = "events4j" } events4j = { module = "com.github.philippheuer.events4j:events4j-handler-simple", version.ref = "events4j" }
kmongo = { module="org.litote.kmongo:kmongo-coroutine-serialization", version.ref = "kmongo"}

View File

@ -3,20 +3,48 @@
*/ */
package dev.jansel.feixiao package dev.jansel.feixiao
import com.github.twitch4j.TwitchClientBuilder
import com.github.twitch4j.events.ChannelChangeTitleEvent
import com.github.twitch4j.events.ChannelGoLiveEvent
import com.github.twitch4j.events.ChannelGoOfflineEvent
import dev.jansel.feixiao.extensions.EventHooks import dev.jansel.feixiao.extensions.EventHooks
import dev.jansel.feixiao.extensions.MessageEvents import dev.jansel.feixiao.extensions.MessageEvents
import dev.jansel.feixiao.utils.* import dev.jansel.feixiao.utils.*
import dev.kordex.core.DATA_COLLECTION import dev.kord.core.behavior.getChannelOf
import dev.kord.core.entity.channel.GuildMessageChannel
import dev.kordex.core.ExtensibleBot import dev.kordex.core.ExtensibleBot
import dev.kordex.data.api.DataCollection import dev.kordex.data.api.DataCollection
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
suspend fun main() { suspend fun main() {
val bot = ExtensibleBot(token) { val bot = ExtensibleBot(token) {
database(true)
dataCollectionMode = DataCollection.None dataCollectionMode = DataCollection.None
extensions { extensions {
add(::MessageEvents) add(::MessageEvents)
add(::EventHooks) add(::EventHooks)
} }
} }
val twitchClient = TwitchClientBuilder.builder()
.withEnableHelix(true)
.withClientId(twitchcid)
.withClientSecret(twitchcs)
.build()
twitchClient.clientHelper.enableStreamEventListener("janselosu")
twitchClient.eventManager.onEvent(ChannelGoLiveEvent::class.java) {
println("${it.channel.name} went live!")
runBlocking {
launch {
val twitchpingschannel =
bot.kordRef.getGuildOrNull(tserverid)?.getChannelOf<GuildMessageChannel>(tchannelid)
twitchpingschannel?.createMessage("<@&1130981452130037800> ${it.channel.name} is now live at https://twitch.tv/${it.channel.name} streaming ${it.stream.gameName}: ${it.stream.title}")
bot.kordRef.editPresence { streaming(it.stream.title, "https://twitch.tv/${it.channel.name}") }
}
}
}
bot.start() bot.start()
} }

View File

@ -0,0 +1,24 @@
package dev.jansel.feixiao.database
import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import dev.jansel.feixiao.utils.mongoUri
import org.bson.UuidRepresentation
import org.litote.kmongo.coroutine.coroutine
import org.litote.kmongo.reactivestreams.KMongo
class Database {
private val settings = MongoClientSettings
.builder()
.uuidRepresentation(UuidRepresentation.STANDARD)
.applyConnectionString(ConnectionString(mongoUri))
.build()
private val client = KMongo.createClient(settings).coroutine
val mongo get() = client.getDatabase("Feixiao")
suspend fun migrate() {
Migrator.migrate()
}
}

View File

@ -0,0 +1,59 @@
package dev.jansel.feixiao.database
import dev.jansel.feixiao.database.collections.MetaCollection
import dev.jansel.feixiao.database.entities.MetaData
import dev.jansel.feixiao.database.migrations.v1
import dev.kordex.core.koin.KordExKoinComponent
import io.github.oshai.kotlinlogging.KotlinLogging
import org.koin.core.component.inject
object Migrator : KordExKoinComponent {
private val logger = KotlinLogging.logger("Migrator Logger")
private val db: Database by inject()
private val mainMetaCollection: MetaCollection by inject()
suspend fun migrate() {
logger.info { "Starting main database migration" }
var meta = mainMetaCollection.get()
if (meta == null) {
meta = MetaData(0)
mainMetaCollection.set(meta)
}
var currentVersion = meta.version
logger.info { "Current main database version: v$currentVersion" }
while (true) {
val nextVersion = currentVersion + 1
@Suppress("TooGenericExceptionCaught", "UseIfInsteadOfWhen")
try {
when (nextVersion) {
1 -> ::v1
else -> break
} (db.mongo)
logger.info { "Migrated database to version $nextVersion." }
} catch (t: Throwable) {
logger.error(t) { "Failed to migrate database to version $nextVersion." }
throw t
}
currentVersion = nextVersion
}
if (currentVersion != meta.version) {
meta = meta.copy(version = currentVersion)
mainMetaCollection.update(meta)
logger.info { "Finished main database migrations." }
}
}
}

View File

@ -0,0 +1,26 @@
package dev.jansel.feixiao.database.collections
import dev.jansel.feixiao.database.Database
import dev.jansel.feixiao.database.entities.MetaData
import dev.kordex.core.koin.KordExKoinComponent
import org.koin.core.component.inject
import org.litote.kmongo.eq
class MetaCollection : KordExKoinComponent {
private val db: Database by inject()
@PublishedApi
internal val collection = db.mongo.getCollection<MetaData>()
suspend fun get(): MetaData? =
collection.findOne()
suspend fun set(meta: MetaData) =
collection.insertOne(meta)
suspend fun update(meta: MetaData) =
collection.findOneAndReplace(
MetaData::id eq "meta",
meta
)
}

View File

@ -0,0 +1,9 @@
package dev.jansel.feixiao.database.entities
import kotlinx.serialization.Serializable
@Serializable
data class MetaData(
val version: Int,
val id: String = "meta"
)

View File

@ -0,0 +1,7 @@
package dev.jansel.feixiao.database.migrations
import org.litote.kmongo.coroutine.CoroutineDatabase
suspend fun v1(db: CoroutineDatabase) {
db.createCollection("meta")
}

View File

@ -1,20 +1,8 @@
package dev.jansel.feixiao.extensions package dev.jansel.feixiao.extensions
import com.github.twitch4j.TwitchClientBuilder
import com.github.twitch4j.events.ChannelChangeTitleEvent
import com.github.twitch4j.events.ChannelGoLiveEvent
import com.github.twitch4j.events.ChannelGoOfflineEvent
import dev.jansel.feixiao.utils.tchannelid
import dev.jansel.feixiao.utils.tserverid
import dev.jansel.feixiao.utils.twitchcid
import dev.jansel.feixiao.utils.twitchcs
import dev.kord.core.behavior.getChannelOf
import dev.kord.core.entity.channel.GuildMessageChannel
import dev.kord.core.event.gateway.ReadyEvent import dev.kord.core.event.gateway.ReadyEvent
import dev.kordex.core.extensions.Extension import dev.kordex.core.extensions.Extension
import dev.kordex.core.extensions.event import dev.kordex.core.extensions.event
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class EventHooks : Extension() { class EventHooks : Extension() {
override val name = "eventhooks" override val name = "eventhooks"
@ -23,40 +11,7 @@ class EventHooks : Extension() {
event<ReadyEvent> { event<ReadyEvent> {
action { action {
println("Bot is ready!") println("Bot is ready!")
kord.editPresence { playing("osu!") } kord.editPresence { "electing a president..." }
val twitchClient = TwitchClientBuilder.builder()
.withEnableHelix(true)
.withClientId(twitchcid)
.withClientSecret(twitchcs)
.build()
twitchClient.clientHelper.enableStreamEventListener("janselosu")
twitchClient.eventManager.onEvent(ChannelGoLiveEvent::class.java) {
println("${it.channel.name} went live!")
runBlocking {
launch {
val twitchpingschannel =
kord.getGuildOrNull(tserverid)?.getChannelOf<GuildMessageChannel>(tchannelid)
twitchpingschannel?.createMessage("<@&1130981452130037800> ${it.channel.name} is now live at https://twitch.tv/${it.channel.name} streaming ${it.stream.gameName}: ${it.stream.title}")
kord.editPresence { streaming(it.stream.title, "https://twitch.tv/${it.channel.name}") }
}
}
}
twitchClient.eventManager.onEvent(ChannelGoOfflineEvent::class.java) {
println("${it.channel.name} went offline.")
runBlocking {
launch {
kord.editPresence { playing("osu!") }
}
}
}
twitchClient.eventManager.onEvent(ChannelChangeTitleEvent::class.java) {
println("Title changed to ${it.title}")
runBlocking {
launch {
kord.editPresence { streaming(it.title, "https://twitch.tv/${it.channel.name}") }
}
}
}
} }
} }
} }

View File

@ -1,10 +1,37 @@
package dev.jansel.feixiao.utils package dev.jansel.feixiao.utils
import dev.jansel.feixiao.database.Database
import dev.jansel.feixiao.database.collections.MetaCollection
import dev.kord.common.entity.Snowflake import dev.kord.common.entity.Snowflake
import dev.kordex.core.builders.ExtensibleBotBuilder
import dev.kordex.core.utils.env import dev.kordex.core.utils.env
import dev.kordex.core.utils.loadModule
import kotlinx.coroutines.runBlocking
import org.koin.dsl.bind
val twitchcid = env("TWITCH_CLIENT_ID") val twitchcid = env("TWITCH_CLIENT_ID")
val twitchcs = env("TWITCH_CLIENT_SECRET") val twitchcs = env("TWITCH_CLIENT_SECRET")
val token = env("TOKEN") val token = env("TOKEN")
val tserverid = Snowflake(env("TEST_SERVER").toLong()) val tserverid = Snowflake(env("TEST_SERVER").toLong())
val tchannelid = Snowflake(env("TEST_CHANNEL").toLong()) val tchannelid = Snowflake(env("TEST_CHANNEL").toLong())
val mongoUri = env("MONGO_URI")
suspend inline fun ExtensibleBotBuilder.database(migrate: Boolean) {
val db = Database()
hooks {
beforeKoinSetup {
loadModule {
single { db } bind Database::class
}
loadModule {
single { MetaCollection() } bind MetaCollection::class
}
if (migrate) {
runBlocking { db.migrate() }
}
}
}
}