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.coroutines)
implementation(libs.twitch4j)
implementation(libs.kmongo)
// Logging dependencies
implementation(libs.groovy)
@ -32,6 +33,8 @@ dependencies {
kordEx {
kordExVersion = "2.3.1-SNAPSHOT"
jvmTarget = 21
bot {
// See https://docs.kordex.dev/data-collection.html
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"
events4j = "0.12.2"
kx-coroutines = "1.9.0"
kmongo = "4.9.0"
[libraries]
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" }
twitch4j = { module = "com.github.twitch4j:twitch4j", version.ref = "twitch4j" }
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
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.MessageEvents
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.data.api.DataCollection
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
suspend fun main() {
val bot = ExtensibleBot(token) {
database(true)
dataCollectionMode = DataCollection.None
extensions {
add(::MessageEvents)
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()
}

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
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.kordex.core.extensions.Extension
import dev.kordex.core.extensions.event
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class EventHooks : Extension() {
override val name = "eventhooks"
@ -23,40 +11,7 @@ class EventHooks : Extension() {
event<ReadyEvent> {
action {
println("Bot is ready!")
kord.editPresence { playing("osu!") }
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}") }
}
}
}
kord.editPresence { "electing a president..." }
}
}
}

View File

@ -1,10 +1,37 @@
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.kordex.core.builders.ExtensibleBotBuilder
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 twitchcs = env("TWITCH_CLIENT_SECRET")
val token = env("TOKEN")
val tserverid = Snowflake(env("TEST_SERVER").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() }
}
}
}
}