1
0

Merge branch 'root' into refactor/different-loading-system

# Conflicts:
#	src/main/kotlin/dev/jansel/feixiao/App.kt
This commit is contained in:
Jannik Reimers 2024-11-28 08:43:47 +01:00
commit a3f31b64d0
Signed by: jansel
GPG Key ID: 39C62D7D5233CFD0
17 changed files with 162 additions and 46 deletions

15
.github/renovate.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"commitBody": "[skip ci]",
"baseBranches": ["refactor/depUpdates"],
"prHourlyLimit": 0,
"packageRules": [
{
"matchUpdateTypes": ["patch", "pin", "digest"],
"automerge": true
}
]
}

View File

@ -29,7 +29,7 @@ jobs:
run: "./gradlew build" run: "./gradlew build"
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Build and Deploy Artifacts name: Build and Deploy Artifacts

View File

@ -26,7 +26,7 @@ jobs:
run: "./gradlew build" run: "./gradlew build"
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Build and Deploy Artifacts name: Build and Deploy Artifacts

View File

@ -31,7 +31,7 @@ jobs:
run: "./gradlew build" run: "./gradlew build"
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
@ -39,7 +39,7 @@ jobs:
password: ${{ secrets.CR_PAT }} password: ${{ secrets.CR_PAT }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v6
with: with:
context: . context: .
platforms: linux/arm64 platforms: linux/arm64

9
.idea/Feixiao.iml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

2
.idea/kotlinc.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="2.0.20" /> <option name="version" value="2.0.21" />
</component> </component>
</project> </project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/feixiao.iml" filepath="$PROJECT_DIR$/.idea/modules/feixiao.iml" />
</modules>
</component>
</project>

8
.idea/modules/feixiao.iml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$/../.." dumb="true">
<excludeFolder url="file://$MODULE_DIR$/../../.kotlin" />
</content>
</component>
</module>

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.events4j)
implementation(libs.kmongo) implementation(libs.kmongo)
// Logging dependencies // Logging dependencies
@ -31,16 +32,20 @@ dependencies {
} }
kordEx { kordEx {
kordExVersion = "2.2.1-SNAPSHOT" kordExVersion = "2.3.1-SNAPSHOT"
jvmTarget = 21 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.None)
mainClass = "dev.jansel.feixiao.AppKt" mainClass = "dev.jansel.feixiao.AppKt"
} }
i18n {
classPackage = "dev.jansel.feixiao.i18n"
translationBundle = "feixiao.strings"
}
} }
// Automatically generate a Dockerfile. Set `generateOnBuild` to `false` if you'd prefer to manually run the // Automatically generate a Dockerfile. Set `generateOnBuild` to `false` if you'd prefer to manually run the

View File

@ -1,16 +1,16 @@
[versions] [versions]
kotlin = "2.0.20" # Note: Plugin versions must be updated in the settings.gradle.kts too kotlin = "2.0.21" # Note: Plugin versions must be updated in the settings.gradle.kts too
groovy = "3.0.22" groovy = "3.0.23"
jansi = "2.4.1" jansi = "2.4.1"
kx-ser = "1.7.3" kx-ser = "1.7.3"
logback = "1.5.12" logback = "1.5.12"
logback-groovy = "1.14.5" logback-groovy = "1.14.5"
logging = "7.0.0" logging = "7.0.0"
twitch4j = "1.22.0" twitch4j = "1.23.0"
events4j = "0.12.2" events4j = "0.12.2"
kx-coroutines = "1.9.0" kx-coroutines = "1.9.0"
kmongo = "4.9.0" kmongo = "5.1.0"
[libraries] [libraries]
groovy = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" } groovy = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
@ -22,5 +22,5 @@ logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
logback-groovy = { module = "io.github.virtualdogbert:logback-groovy-config", version.ref = "logback-groovy" } logback-groovy = { module = "io.github.virtualdogbert:logback-groovy-config", version.ref = "logback-groovy" }
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-reactor", version.ref = "events4j" }
kmongo = { module="org.litote.kmongo:kmongo-coroutine-serialization", version.ref = "kmongo"} kmongo = { module="org.litote.kmongo:kmongo-coroutine-serialization", version.ref = "kmongo"}

View File

@ -1,14 +1,20 @@
pluginManagement { pluginManagement {
plugins { plugins {
// Update this in libs.version.toml when you change it here. // Update this in libs.version.toml when you change it here.
kotlin("jvm") version "2.0.20" kotlin("jvm") version "2.0.21"
kotlin("plugin.serialization") version "2.0.20" kotlin("plugin.serialization") version "2.0.21"
id("com.github.jakemarsden.git-hooks") version "0.0.2"
id("com.github.johnrengelman.shadow") version "8.1.1" id("com.github.johnrengelman.shadow") version "8.1.1"
id("dev.kordex.gradle.docker") version "1.4.2" id("dev.kordex.gradle.docker") version "1.5.8"
id("dev.kordex.gradle.kordex") version "1.4.2" id("dev.kordex.gradle.kordex") version "1.5.8"
}
repositories {
gradlePluginPortal()
mavenCentral()
maven("https://releases-repo.kordex.dev")
maven("https://snapshots-repo.kordex.dev")
} }
} }

View File

@ -3,16 +3,20 @@
*/ */
package dev.jansel.feixiao package dev.jansel.feixiao
import com.github.philippheuer.events4j.reactor.ReactorEventHandler
import com.github.twitch4j.TwitchClient import com.github.twitch4j.TwitchClient
import com.github.twitch4j.TwitchClientBuilder import com.github.twitch4j.TwitchClientBuilder
import com.github.twitch4j.events.ChannelGoLiveEvent import com.github.twitch4j.events.ChannelGoLiveEvent
import dev.jansel.feixiao.database.collections.StreamerCollection import dev.jansel.feixiao.database.collections.StreamerCollection
import dev.jansel.feixiao.extensions.EventHooks import dev.jansel.feixiao.extensions.EventHooks
import dev.jansel.feixiao.extensions.StreamerCommand import dev.jansel.feixiao.extensions.StreamerCommand
import dev.jansel.feixiao.utils.* import dev.jansel.feixiao.utils.database
import dev.jansel.feixiao.utils.token
import dev.jansel.feixiao.utils.twitchcid
import dev.jansel.feixiao.utils.twitchcs
import dev.kord.core.entity.channel.GuildMessageChannel 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.core.i18n.SupportedLocales
import io.github.oshai.kotlinlogging.KotlinLogging import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -25,11 +29,13 @@ suspend fun main() {
botRef = ExtensibleBot(token) { botRef = ExtensibleBot(token) {
database(true) database(true)
twitch(true) twitch(true)
dataCollectionMode = DataCollection.None
extensions { extensions {
add(::EventHooks) add(::EventHooks)
add(::StreamerCommand) add(::StreamerCommand)
} }
i18n {
applicationCommandLocale(SupportedLocales.ENGLISH, SupportedLocales.GERMAN)
}
} }
botRef!!.start() botRef!!.start()

View File

@ -20,26 +20,38 @@ class StreamerCollection : KordExKoinComponent {
suspend fun getData(channelName: String): StreamerData? = suspend fun getData(channelName: String): StreamerData? =
collection.findOne(StreamerData::name eq channelName) collection.findOne(StreamerData::name eq channelName)
suspend fun updateData(guildId: Snowflake, channelId: Snowflake, streamerName: String, roleId: Snowflake?) { suspend fun updateData(
guildId: Snowflake,
channelId: Snowflake,
streamerName: String,
roleId: Snowflake?,
liveMessage: String?
) {
val coll = collection.findOne(StreamerData::name eq streamerName) val coll = collection.findOne(StreamerData::name eq streamerName)
if (coll != null) { if (coll != null) {
collection.updateOne( collection.updateOne(
StreamerData::name eq streamerName, StreamerData::name eq streamerName,
setValue(StreamerData::servers, coll.servers + listOf(Server(guildId, channelId, roleId))) setValue(StreamerData::servers, coll.servers + listOf(Server(guildId, channelId, roleId, liveMessage)))
) )
} else { } else {
collection.insertOne( collection.insertOne(
StreamerData(streamerName, listOf(Server(guildId, channelId, roleId))) StreamerData(streamerName, listOf(Server(guildId, channelId, roleId, liveMessage)))
) )
} }
} }
suspend fun removeData(guildId: Snowflake, channelId: Snowflake, streamerName: String, roleId: Snowflake?) { suspend fun removeData(
guildId: Snowflake,
channelId: Snowflake,
streamerName: String,
roleId: Snowflake?,
liveMessage: String?
) {
val coll = collection.findOne(StreamerData::name eq streamerName) val coll = collection.findOne(StreamerData::name eq streamerName)
if (coll != null) { if (coll != null) {
collection.updateOne( collection.updateOne(
StreamerData::name eq streamerName, StreamerData::name eq streamerName,
setValue(StreamerData::servers, coll.servers - Server(guildId, channelId, roleId)) setValue(StreamerData::servers, coll.servers - Server(guildId, channelId, roleId, liveMessage))
) )
} }
} }

View File

@ -13,5 +13,6 @@ data class StreamerData(
data class Server( data class Server(
val guildId: Snowflake, val guildId: Snowflake,
val channelId: Snowflake, val channelId: Snowflake,
val roleId: Snowflake? val roleId: Snowflake?,
val liveMessage: String?
) )

View File

@ -1,11 +1,17 @@
package dev.jansel.feixiao.extensions package dev.jansel.feixiao.extensions
import dev.jansel.feixiao.database.collections.StreamerCollection import dev.jansel.feixiao.database.collections.StreamerCollection
import dev.jansel.feixiao.database.entities.StreamerData
import dev.jansel.feixiao.logger import dev.jansel.feixiao.logger
import dev.jansel.feixiao.twitchClient import dev.jansel.feixiao.twitchClient
import dev.jansel.feixiao.utils.tchannelid
import dev.jansel.feixiao.utils.tserverid
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 org.litote.kmongo.eq
class EventHooks : Extension() { class EventHooks : Extension() {
override val name = "eventhooks" override val name = "eventhooks"
@ -14,11 +20,19 @@ class EventHooks : Extension() {
event<ReadyEvent> { event<ReadyEvent> {
action { action {
logger.info { "Bot is ready!" } logger.info { "Bot is ready!" }
val onlineLog =
kord.getGuildOrNull(tserverid)?.getChannelOf<GuildMessageChannel>(tchannelid)
onlineLog?.createMessage("Bot Online!")
kord.editPresence { listening("the database") } kord.editPresence { listening("the database") }
// check every entry in the database and enable the stream event listener // check every entry in the database and enable the stream event listener if a server is listening to the streamer
StreamerCollection().collection.find().toList().forEach { StreamerCollection().collection.find().toList().forEach {
if (it.servers.isNotEmpty()) {
twitchClient!!.clientHelper.enableStreamEventListener(it.name) twitchClient!!.clientHelper.enableStreamEventListener(it.name)
logger.info { "Enabled stream event listener for ${it.name}" } logger.info { "Enabled stream event listener for ${it.name}" }
} else {
logger.info { "No servers are listening to ${it.name}, deleting from the database..." }
StreamerCollection().collection.deleteMany(StreamerData::name eq it.name)
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
package dev.jansel.feixiao.extensions package dev.jansel.feixiao.extensions
import dev.jansel.feixiao.database.collections.StreamerCollection import dev.jansel.feixiao.database.collections.StreamerCollection
import dev.jansel.feixiao.i18n.Translations
import dev.jansel.feixiao.twitchClient import dev.jansel.feixiao.twitchClient
import dev.kord.common.entity.Permission import dev.kord.common.entity.Permission
import dev.kordex.core.checks.anyGuild import dev.kordex.core.checks.anyGuild
@ -9,6 +10,7 @@ import dev.kordex.core.commands.Arguments
import dev.kordex.core.commands.application.slash.publicSubCommand import dev.kordex.core.commands.application.slash.publicSubCommand
import dev.kordex.core.commands.converters.impl.channel import dev.kordex.core.commands.converters.impl.channel
import dev.kordex.core.commands.converters.impl.optionalRole import dev.kordex.core.commands.converters.impl.optionalRole
import dev.kordex.core.commands.converters.impl.optionalString
import dev.kordex.core.commands.converters.impl.string import dev.kordex.core.commands.converters.impl.string
import dev.kordex.core.extensions.Extension import dev.kordex.core.extensions.Extension
import dev.kordex.core.extensions.publicSlashCommand import dev.kordex.core.extensions.publicSlashCommand
@ -17,19 +19,25 @@ class StreamerCommand : Extension() {
override val name = "streaming" override val name = "streaming"
override suspend fun setup() { override suspend fun setup() {
publicSlashCommand { publicSlashCommand {
name = "streamer" name = Translations.Streamer.Command.name
description = "Streamer commands" description = Translations.Streamer.Command.description
publicSubCommand(::AddStreamerArgs) { publicSubCommand(::AddStreamerArgs) {
name = "add" name = Translations.Streamer.Command.Add.name
description = "Add a streamer to the listener of this server" description = Translations.Streamer.Command.Add.description
check { check {
anyGuild() anyGuild()
hasPermission(Permission.ManageGuild) hasPermission(Permission.ManageGuild)
} }
action { action {
val streamer = arguments.streamer val streamer = arguments.streamer
StreamerCollection().updateData(guild!!.id, arguments.channel.id, streamer, arguments.role?.id) StreamerCollection().updateData(
guild!!.id,
arguments.channel.id,
streamer,
arguments.role?.id,
arguments.message
)
twitchClient!!.clientHelper.enableStreamEventListener(streamer) twitchClient!!.clientHelper.enableStreamEventListener(streamer)
respond { respond {
content = "Added streamer $streamer" content = "Added streamer $streamer"
@ -38,15 +46,15 @@ class StreamerCommand : Extension() {
} }
publicSubCommand(::RemoveStreamerArgs) { publicSubCommand(::RemoveStreamerArgs) {
name = "remove" name = Translations.Streamer.Command.Remove.name
description = "Remove a streamer from the listener of this server" description = Translations.Streamer.Command.Remove.description
check { check {
anyGuild() anyGuild()
hasPermission(Permission.ManageGuild) hasPermission(Permission.ManageGuild)
} }
action { action {
val streamer = arguments.streamer val streamer = arguments.streamer
StreamerCollection().removeData(guild!!.id, channel.id, streamer, null) StreamerCollection().removeData(guild!!.id, channel.id, streamer, null, null)
respond { respond {
content = "Removed streamer $streamer" content = "Removed streamer $streamer"
} }
@ -57,25 +65,29 @@ class StreamerCommand : Extension() {
inner class AddStreamerArgs : Arguments() { inner class AddStreamerArgs : Arguments() {
val streamer by string { val streamer by string {
name = "streamer" name = Translations.Streamer.Command.Arguments.Add.Streamer.name
description = "The streamer to add" description = Translations.Streamer.Command.Arguments.Add.Streamer.description
require(true) require(true)
} }
val channel by channel { val channel by channel {
name = "announcechannel" name = Translations.Streamer.Command.Arguments.Add.Channel.name
description = "Channel where the bot will send a message when the streamer goes live" description = Translations.Streamer.Command.Arguments.Add.Channel.description
require(true) require(true)
} }
val role by optionalRole { val role by optionalRole {
name = "role" name = Translations.Streamer.Command.Arguments.Add.Role.name
description = "Role to ping when the streamer goes live" description = Translations.Streamer.Command.Arguments.Add.Role.description
}
val message by optionalString {
name = Translations.Streamer.Command.Arguments.Add.Message.name
description = Translations.Streamer.Command.Arguments.Add.Message.description
} }
} }
inner class RemoveStreamerArgs : Arguments() { inner class RemoveStreamerArgs : Arguments() {
val streamer by string { val streamer by string {
name = "streamer" name = Translations.Streamer.Command.Arguments.Remove.name
description = "The streamer to remove" description = Translations.Streamer.Command.Arguments.Remove.description
require(true) require(true)
} }
} }

View File

@ -0,0 +1,20 @@
# Streamer Command
streamer.command.name=streamer
streamer.command.description=A bundle of Streamer commands
streamer.command.add.name=add
streamer.command.add.description=Add a new streamer to the listener
streamer.command.remove.name=remove
streamer.command.remove.description=Remove a streamer from the listener
streamer.command.arguments.add.streamer.name=streamer
streamer.command.arguments.add.streamer.description=The streamer to add
streamer.command.arguments.add.channel.name=channel
streamer.command.arguments.add.channel.description=The channel to add the streamer to
streamer.command.arguments.add.role.name=role
streamer.command.arguments.add.role.description=The role to assign to the streamer
streamer.command.arguments.add.message.name=message
streamer.command.arguments.add.message.description=Custom Announce message. Placeholders (in curly braces): url, name, title, category, role (if set)
streamer.command.arguments.remove.name=streamer
streamer.command.arguments.remove.description=The streamer to remove
# more to come...