This commit is contained in:
parent
5ad150e53d
commit
61c9fa6731
@ -23,6 +23,8 @@ dependencies {
|
||||
implementation(libs.kx.ser)
|
||||
implementation(libs.twitch4j)
|
||||
implementation(libs.events4j)
|
||||
implementation(libs.kx.coroutines)
|
||||
implementation(libs.kmongo)
|
||||
|
||||
// Logging dependencies
|
||||
implementation(libs.groovy)
|
||||
|
@ -560,7 +560,7 @@ style:
|
||||
active: true
|
||||
maxJumpCount: 3
|
||||
MagicNumber:
|
||||
active: true
|
||||
active: false
|
||||
excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
|
||||
ignoreNumbers: [ '-1', '0', '1', '2' ]
|
||||
ignoreHashCodeFunction: true
|
||||
@ -625,7 +625,7 @@ style:
|
||||
TrailingWhitespace:
|
||||
active: true
|
||||
UnderscoresInNumericLiterals:
|
||||
active: true
|
||||
active: false
|
||||
acceptableDecimalLength: 5
|
||||
UnnecessaryAbstractClass:
|
||||
active: true
|
||||
|
@ -12,6 +12,9 @@ kordex-gradle = "1.7.0"
|
||||
kordex = "2.3.1-SNAPSHOT"
|
||||
twitch4j = "1.24.0"
|
||||
events4j = "0.12.2"
|
||||
kx-coroutines = "1.9.0"
|
||||
kmongo = "5.1.0"
|
||||
|
||||
|
||||
|
||||
[libraries]
|
||||
@ -25,6 +28,10 @@ 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-reactor", version.ref = "events4j" }
|
||||
kmongo = { module="org.litote.kmongo:kmongo-coroutine-serialization", version.ref = "kmongo" }
|
||||
kx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kx-coroutines" }
|
||||
|
||||
|
||||
|
||||
|
||||
[plugins]
|
||||
|
@ -4,7 +4,7 @@
|
||||
package dev.jansel.aglaea
|
||||
|
||||
import com.github.twitch4j.TwitchClient
|
||||
import dev.jansel.aglaea.extensions.TestExtension
|
||||
import dev.jansel.aglaea.extensions.ReplayExtension
|
||||
import dev.jansel.aglaea.utils.twitch
|
||||
import dev.kord.common.entity.Snowflake
|
||||
import dev.kordex.core.ExtensibleBot
|
||||
@ -25,7 +25,7 @@ private val TOKEN = env("TOKEN") // Get the bot' token from the env vars or a
|
||||
suspend fun main() {
|
||||
val bot = ExtensibleBot(TOKEN) {
|
||||
extensions {
|
||||
add(::TestExtension)
|
||||
add(::ReplayExtension)
|
||||
}
|
||||
twitch(true)
|
||||
if (devMode) {
|
||||
|
24
src/main/kotlin/dev/jansel/aglaea/database/Database.kt
Normal file
24
src/main/kotlin/dev/jansel/aglaea/database/Database.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package dev.jansel.aglaea.database
|
||||
|
||||
import com.mongodb.ConnectionString
|
||||
import com.mongodb.MongoClientSettings
|
||||
import dev.jansel.aglaea.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("Aglaea")
|
||||
|
||||
suspend fun migrate() {
|
||||
Migrator.migrate()
|
||||
}
|
||||
}
|
71
src/main/kotlin/dev/jansel/aglaea/database/Migrator.kt
Normal file
71
src/main/kotlin/dev/jansel/aglaea/database/Migrator.kt
Normal file
@ -0,0 +1,71 @@
|
||||
package dev.jansel.aglaea.database
|
||||
|
||||
import com.github.philippheuer.events4j.reactor.ReactorEventHandler
|
||||
import com.github.twitch4j.TwitchClientBuilder
|
||||
import dev.jansel.aglaea.database.collections.MetaCollection
|
||||
import dev.jansel.aglaea.database.entities.MetaData
|
||||
import dev.jansel.aglaea.database.migrations.v1
|
||||
import dev.jansel.aglaea.twitchClient
|
||||
import dev.jansel.aglaea.utils.twitchcid
|
||||
import dev.jansel.aglaea.utils.twitchcs
|
||||
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" }
|
||||
logger.info { "Initializing Twitch client just in case" }
|
||||
twitchClient = TwitchClientBuilder.builder()
|
||||
.withEnableHelix(true)
|
||||
.withDefaultEventHandler(ReactorEventHandler::class.java)
|
||||
.withClientId(twitchcid)
|
||||
.withClientSecret(twitchcs)
|
||||
.build()
|
||||
|
||||
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." }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package dev.jansel.aglaea.database.collections
|
||||
|
||||
import dev.jansel.aglaea.database.Database
|
||||
import dev.jansel.aglaea.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
|
||||
)
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package dev.jansel.aglaea.database.collections
|
||||
|
||||
import dev.jansel.aglaea.database.Database
|
||||
import dev.jansel.aglaea.database.entities.ReplayData
|
||||
import dev.kordex.core.koin.KordExKoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.litote.kmongo.eq
|
||||
import org.litote.kmongo.exists
|
||||
|
||||
class ReplayCollection : KordExKoinComponent {
|
||||
private val db by inject<Database>()
|
||||
|
||||
@PublishedApi
|
||||
internal val collection = db.mongo.getCollection<ReplayData>()
|
||||
suspend fun getAll(): List<ReplayData> =
|
||||
collection.find().toList()
|
||||
suspend fun get(id: String): ReplayData? =
|
||||
collection.findOne(ReplayData::id eq id)
|
||||
suspend fun set(replay: ReplayData) =
|
||||
collection.insertOne(replay)
|
||||
suspend fun update(replay: ReplayData) =
|
||||
collection.findOneAndReplace(
|
||||
ReplayData::id eq replay.id,
|
||||
replay
|
||||
)
|
||||
suspend fun delete(id: String) =
|
||||
collection.deleteOne(ReplayData::id eq id)
|
||||
suspend fun deleteAll() =
|
||||
collection.deleteMany(ReplayData::id exists true)
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package dev.jansel.aglaea.database.entities
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MetaData(
|
||||
val version: Int,
|
||||
val id: String = "meta"
|
||||
)
|
@ -0,0 +1,27 @@
|
||||
package dev.jansel.aglaea.database.entities
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReplayData(
|
||||
val id: String,
|
||||
val replayFile: ByteArray,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ReplayData
|
||||
|
||||
if (id != other.id) return false
|
||||
if (!replayFile.contentEquals(other.replayFile)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id.hashCode()
|
||||
result = 31 * result + replayFile.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package dev.jansel.aglaea.database.migrations
|
||||
|
||||
import org.litote.kmongo.coroutine.CoroutineDatabase
|
||||
|
||||
suspend fun v1(db: CoroutineDatabase) {
|
||||
db.createCollection("metaData")
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
package dev.jansel.aglaea.extensions
|
||||
|
||||
import com.github.philippheuer.credentialmanager.domain.OAuth2Credential
|
||||
import dev.jansel.aglaea.logger
|
||||
import dev.jansel.aglaea.twitchClient
|
||||
import dev.jansel.aglaea.utils.twitchcid
|
||||
import dev.jansel.aglaea.utils.twitchcs
|
||||
import dev.kord.core.event.gateway.ReadyEvent
|
||||
import dev.kordex.core.extensions.Extension
|
||||
import dev.kordex.core.extensions.event
|
||||
@ -18,7 +14,6 @@ class EventHooks : Extension() {
|
||||
// This is where you can add any code that should run when the bot is ready
|
||||
// For example, you can initialize any services or start any background tasks
|
||||
logger.info { "Bot is ready!" }
|
||||
twitchClient!!.pubSub.listenForChannelPointsRedemptionEvents(OAuth2Credential(twitchcid, twitchcs), "120275141")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
package dev.jansel.aglaea.extensions
|
||||
|
||||
import dev.jansel.aglaea.database.collections.ReplayCollection
|
||||
import dev.jansel.aglaea.database.entities.ReplayData
|
||||
import dev.jansel.aglaea.i18n.Translations
|
||||
import dev.kord.common.entity.Snowflake
|
||||
import dev.kordex.core.checks.inChannel
|
||||
import dev.kordex.core.commands.Arguments
|
||||
import dev.kordex.core.commands.converters.impl.attachment
|
||||
import dev.kordex.core.extensions.Extension
|
||||
import dev.kordex.core.extensions.ephemeralSlashCommand
|
||||
import dev.kordex.core.utils.download
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
class ReplayExtension : Extension() {
|
||||
override val name = "test"
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class)
|
||||
override suspend fun setup() {
|
||||
ephemeralSlashCommand(::ReplayArgs) {
|
||||
check {
|
||||
inChannel(Snowflake(1130954956892029060))
|
||||
}
|
||||
action {
|
||||
val file = arguments.file
|
||||
if (file.filename.endsWith(".osr")) {
|
||||
val id = Uuid.random().toString()
|
||||
|
||||
ReplayCollection().set(ReplayData(id, file.download()))
|
||||
respond {
|
||||
content = "Your Auth Code is: $id"
|
||||
}
|
||||
} else {
|
||||
respond {
|
||||
content = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class ReplayArgs : Arguments() {
|
||||
val file by attachment {
|
||||
name = Translations.Arguments.File.name
|
||||
description = Translations.Arguments.File.description
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
package dev.jansel.aglaea.extensions
|
||||
|
||||
import dev.jansel.aglaea.TEST_SERVER_ID
|
||||
import dev.jansel.aglaea.i18n.Translations
|
||||
import dev.kordex.core.commands.Arguments
|
||||
import dev.kordex.core.commands.converters.impl.coalescingDefaultingString
|
||||
import dev.kordex.core.commands.converters.impl.defaultingString
|
||||
import dev.kordex.core.commands.converters.impl.user
|
||||
import dev.kordex.core.components.components
|
||||
import dev.kordex.core.components.publicButton
|
||||
import dev.kordex.core.extensions.Extension
|
||||
import dev.kordex.core.extensions.publicSlashCommand
|
||||
import dev.kordex.core.i18n.withContext
|
||||
|
||||
class TestExtension : Extension() {
|
||||
override val name = "test"
|
||||
|
||||
override suspend fun setup() {
|
||||
publicSlashCommand(::SlapSlashArgs) {
|
||||
name = Translations.Commands.Slap.name
|
||||
description = Translations.Commands.Slap.description
|
||||
|
||||
guild(TEST_SERVER_ID) // Otherwise it will take up to an hour to update
|
||||
|
||||
action {
|
||||
// Don't slap ourselves on request, slap the requester!
|
||||
val realTarget = if (arguments.target.id == event.kord.selfId) {
|
||||
member
|
||||
} else {
|
||||
arguments.target
|
||||
}
|
||||
|
||||
respond {
|
||||
content = Translations.Commands.Slap.response
|
||||
.withContext(this@action)
|
||||
.translateNamed(
|
||||
"target" to realTarget?.mention,
|
||||
"weapon" to arguments.weapon
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publicSlashCommand {
|
||||
name = Translations.Commands.Button.name
|
||||
description = Translations.Commands.Button.description
|
||||
|
||||
action {
|
||||
respond {
|
||||
components {
|
||||
publicButton {
|
||||
label = Translations.Components.Button.label
|
||||
.withLocale(this@action.getLocale())
|
||||
|
||||
action {
|
||||
respond {
|
||||
content = Translations.Components.Button.response
|
||||
.withLocale(getLocale())
|
||||
.translate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class SlapArgs : Arguments() {
|
||||
val target by user {
|
||||
name = Translations.Arguments.Target.name
|
||||
description = Translations.Arguments.Target.description
|
||||
}
|
||||
|
||||
val weapon by coalescingDefaultingString {
|
||||
name = Translations.Arguments.Weapon.name
|
||||
|
||||
defaultValue = "🐟"
|
||||
description = Translations.Arguments.Weapon.description
|
||||
}
|
||||
}
|
||||
|
||||
inner class SlapSlashArgs : Arguments() {
|
||||
val target by user {
|
||||
name = Translations.Arguments.Target.name
|
||||
description = Translations.Arguments.Target.description
|
||||
}
|
||||
|
||||
// Slash commands don't support coalescing strings
|
||||
val weapon by defaultingString {
|
||||
name = Translations.Arguments.Weapon.name
|
||||
|
||||
defaultValue = "🐟"
|
||||
description = Translations.Arguments.Weapon.description
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package dev.jansel.aglaea.utils
|
||||
import com.github.philippheuer.credentialmanager.domain.OAuth2Credential
|
||||
import com.github.twitch4j.TwitchClientBuilder
|
||||
import com.github.twitch4j.chat.events.channel.ChannelMessageEvent
|
||||
import com.github.twitch4j.graphql.internal.type.CommunityPointsCustomRewardRedemptionStatus
|
||||
import dev.jansel.aglaea.logger
|
||||
import dev.jansel.aglaea.twitchClient
|
||||
import dev.kordex.core.koin.KordExKoinComponent
|
||||
@ -24,12 +23,6 @@ class Twitch : KordExKoinComponent {
|
||||
twitchClient!!.eventManager.onEvent(ChannelMessageEvent::class.java) { event ->
|
||||
if (event.customRewardId.isPresent && event.customRewardId.get() == "38157e62-de35-4a21-8200-447b55d7577e") {
|
||||
logger.info { "Channel points redeemed: ${event.customRewardId.get()}" }
|
||||
twitchClient!!.graphQL.updateRedemptionStatus(
|
||||
OAuth2Credential("twitch", mainTwitchToken),
|
||||
"120275141",
|
||||
event.eventId,
|
||||
CommunityPointsCustomRewardRedemptionStatus.FULFILLED
|
||||
).execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,8 @@ import org.koin.dsl.bind
|
||||
val twitchcid = env("TWITCH_CLIENT_ID")
|
||||
val twitchcs = env("TWITCH_CLIENT_SECRET")
|
||||
val twitchToken = env("TWITCH_OAUTH_TOKEN")
|
||||
val mongoUri = env("MONGO_URI")
|
||||
|
||||
// This is the token for the main account, because of ChannelPoint stuff
|
||||
val mainTwitchToken = env("TWITCH_OAUTH_TOKEN_MAIN")
|
||||
suspend inline fun ExtensibleBotBuilder.twitch(active: Boolean) {
|
||||
hooks {
|
||||
beforeKoinSetup {
|
||||
|
@ -8,8 +8,5 @@ commands.button.description=A simple example command, which sends a button.
|
||||
components.button.label=Button!
|
||||
components.button.response=You pushed the button!
|
||||
|
||||
arguments.target.name=target
|
||||
arguments.target.description=User you want to slap
|
||||
|
||||
arguments.weapon.name=weapon
|
||||
arguments.weapon.description=What you want to slap with
|
||||
arguments.file.name=file
|
||||
arguments.file.description=The file to upload
|
||||
|
Loading…
x
Reference in New Issue
Block a user