Skip to content

Commit

Permalink
feat: allow pausing specific server status monitors
Browse files Browse the repository at this point in the history
* rollback to kotlin 1.6.21 as kord does not seem to support kotlin 1.7.0 yet
  • Loading branch information
DarkAtra committed Jun 11, 2022
1 parent 0735e8b commit 20fc84f
Show file tree
Hide file tree
Showing 15 changed files with 158 additions and 61 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ This bot allows you to display some information about your v rising server on di

## Commands

| Command | Description |
|---------------------------------------------------------------------------------------------------------------|-------------------------------------------|
| `/list-servers` | Lists all server status monitors. |
| `/add-server <server-hostname> <server-query-port> <display-player-gear-level>` | Adds a server to the status monitor. |
| `/update-server <server-status-monitor-id> <server-hostname> <server-query-port> <display-player-gear-level>` | Updates the given server status monitor. |
| `/remove-server <server-status-monitor-id>` | Removes a server from the status monitor. |
| Command | Description |
|------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|
| `/list-servers` | Lists all server status monitors. |
| `/add-server <server-hostname> <server-query-port> <display-player-gear-level>` | Adds a server to the status monitor. |
| `/update-server <server-status-monitor-id> <server-hostname> <server-query-port> <status> <display-player-gear-level>` | Updates the given server status monitor. |
| `/remove-server <server-status-monitor-id>` | Removes a server from the status monitor. |

Please note that all commands are [guild](https://discord.com/developers/docs/resources/guild) specific.

Expand Down
9 changes: 7 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
Expand Down Expand Up @@ -32,7 +32,7 @@
<image.name>ghcr.io/darkatra/${project.artifactId}:${project.version}</image.name>

<java.version>11</java.version>
<kotlin.version>1.7.0</kotlin.version>
<kotlin.version>1.6.21</kotlin.version>
<kotlin.code.style>official</kotlin.code.style>

<kord.version>0.8.0-M14</kord.version>
Expand All @@ -57,6 +57,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

<!-- Discord -->
<dependency>
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/de/darkatra/vrising/discord/Bot.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.darkatra.vrising.discord

import de.darkatra.vrising.discord.command.Command
import de.darkatra.vrising.discord.migration.DatabaseMigrationService
import dev.kord.core.Kord
import dev.kord.core.behavior.interaction.response.respond
import dev.kord.core.event.gateway.ReadyEvent
Expand Down

This file was deleted.

14 changes: 14 additions & 0 deletions src/main/kotlin/de/darkatra/vrising/discord/DatabaseUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.darkatra.vrising.discord

import org.dizitart.kno2.filters.and
import org.dizitart.no2.objects.ObjectFilter

operator fun ObjectFilter?.plus(other: ObjectFilter?): ObjectFilter? {
if (this == null && other == null) {
return null
}
if (this != null && other != null) {
return this.and(other)
}
return this ?: other
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ package de.darkatra.vrising.discord
import org.dizitart.no2.IndexType
import org.dizitart.no2.objects.Id
import org.dizitart.no2.objects.Index
import org.dizitart.no2.objects.Indices

@Index(value = "discordServerId", type = IndexType.NonUnique)
@Indices(
value = [
Index(value = "discordServerId", type = IndexType.NonUnique),
Index(value = "status", type = IndexType.NonUnique)
]
)
data class ServerStatusMonitor(
@Id
val id: String,
Expand All @@ -13,6 +19,7 @@ data class ServerStatusMonitor(

val hostName: String,
val queryPort: Int,
val status: ServerStatusMonitorStatus,
val displayPlayerGearLevel: Boolean,

var currentEmbedMessageId: String? = null,
Expand All @@ -25,6 +32,7 @@ data class ServerStatusMonitor(
discordChannelId = discordChannelId,
hostName = hostName,
queryPort = queryPort,
status = status,
displayPlayerGearLevel = displayPlayerGearLevel,
currentEmbedMessageId = currentEmbedMessageId
)
Expand All @@ -38,6 +46,7 @@ class ServerStatusMonitorBuilder(

var hostName: String,
var queryPort: Int,
var status: ServerStatusMonitorStatus,
var displayPlayerGearLevel: Boolean,

var currentEmbedMessageId: String? = null,
Expand All @@ -50,6 +59,7 @@ class ServerStatusMonitorBuilder(
discordChannelId = discordChannelId,
hostName = hostName,
queryPort = queryPort,
status = status,
displayPlayerGearLevel = displayPlayerGearLevel,
currentEmbedMessageId = currentEmbedMessageId
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.dizitart.kno2.filters.and
import org.dizitart.no2.Nitrite
import org.dizitart.no2.objects.ObjectFilter
import org.dizitart.no2.objects.filters.ObjectFilters
import org.springframework.stereotype.Service
import java.time.Duration
Expand Down Expand Up @@ -42,19 +43,29 @@ class ServerStatusMonitorService(
return repository.find(ObjectFilters.eq("id", id).and(ObjectFilters.eq("discordServerId", discordServerId))).firstOrNull()
}

fun getServerStatusMonitors(discordServerId: String? = null): List<ServerStatusMonitor> {
fun getServerStatusMonitors(discordServerId: String? = null, status: ServerStatusMonitorStatus? = null): List<ServerStatusMonitor> {

var objectFilter: ObjectFilter? = null

// apply filters
if (discordServerId != null) {
return repository.find(ObjectFilters.eq("discordServerId", discordServerId)).toList()
objectFilter = ObjectFilters.eq("discordServerId", discordServerId)
}
if (status != null) {
objectFilter += ObjectFilters.eq("status", status)
}

return repository.find().toList()
return when {
objectFilter != null -> repository.find(objectFilter).toList()
else -> repository.find().toList()
}
}

fun launchServerStatusMonitor(kord: Kord) {
launch {
while (isActive) {
runCatching {
getServerStatusMonitors().forEach { serverStatusConfiguration ->
getServerStatusMonitors(status = ServerStatusMonitorStatus.ACTIVE).forEach { serverStatusConfiguration ->

val channel = kord.getChannel(Snowflake(serverStatusConfiguration.discordChannelId))
if (channel == null || channel !is MessageChannelBehavior) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package de.darkatra.vrising.discord

enum class ServerStatusMonitorStatus {
INACTIVE,
ACTIVE
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package de.darkatra.vrising.discord.command
import com.fasterxml.uuid.Generators
import de.darkatra.vrising.discord.ServerStatusMonitor
import de.darkatra.vrising.discord.ServerStatusMonitorService
import de.darkatra.vrising.discord.ServerStatusMonitorStatus
import dev.kord.core.Kord
import dev.kord.core.behavior.interaction.response.respond
import dev.kord.core.entity.interaction.ChatInputCommandInteraction
Expand Down Expand Up @@ -49,12 +50,13 @@ class AddServerCommand(
discordChannelId = channelId.toString(),
hostName = hostName,
queryPort = queryPort,
status = ServerStatusMonitorStatus.ACTIVE,
displayPlayerGearLevel = displayPlayerGearLevel
)
)

interaction.deferEphemeralResponse().respond {
content = "Added monitor for '${hostName}:${queryPort}' to channel '$channelId'. It might take up to 1 minute for the status post to appear."
content = "Added monitor for '${hostName}:${queryPort}' to channel '$channelId'. It may take up to 1 minute for the status message to appear."
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.darkatra.vrising.discord.command

import de.darkatra.vrising.discord.ServerStatusMonitorStatus
import dev.kord.core.entity.interaction.ChatInputCommandInteraction
import dev.kord.rest.builder.interaction.GlobalChatInputCreateBuilder
import dev.kord.rest.builder.interaction.boolean
Expand All @@ -9,6 +10,7 @@ import dev.kord.rest.builder.interaction.string
private const val SERVER_STATUS_MONITOR_ID_PARAMETER_NAME = "server-status-monitor-id"
private const val SERVER_HOSTNAME_PARAMETER_NAME = "server-hostname"
private const val SERVER_QUERY_PORT_PARAMETER_NAME = "server-query-port"
private const val SERVER_STATUS_MONITOR_STATUS_PARAMETER_NAME = "status"
private const val DISPLAY_PLAYER_GEAR_LEVEL_PARAMETER_NAME = "display-player-gear-level"

fun GlobalChatInputCreateBuilder.addServerStatusMonitorIdParameter() {
Expand Down Expand Up @@ -62,3 +64,16 @@ fun GlobalChatInputCreateBuilder.addDisplayPlayerGearLevelParameter(required: Bo
fun ChatInputCommandInteraction.getDisplayPlayerGearLevelParameter(): Boolean? {
return command.booleans[DISPLAY_PLAYER_GEAR_LEVEL_PARAMETER_NAME]
}

fun GlobalChatInputCreateBuilder.addServerStatusMonitorStatusParameter(required: Boolean = true) {
string(
name = SERVER_STATUS_MONITOR_STATUS_PARAMETER_NAME,
description = "The status of the server status monitor. Either ACTIVE or INACTIVE."
) {
this.required = required
}
}

fun ChatInputCommandInteraction.getServerStatusMonitorStatusParameter(): ServerStatusMonitorStatus? {
return command.strings[SERVER_STATUS_MONITOR_STATUS_PARAMETER_NAME]?.let { ServerStatusMonitorStatus.valueOf(it) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class UpdateServerCommand(

addServerHostnameParameter(required = false)
addServerQueryPortParameter(required = false)
addServerStatusMonitorStatusParameter(required = false)
addDisplayPlayerGearLevelParameter(required = false)
}
}
Expand All @@ -37,6 +38,7 @@ class UpdateServerCommand(
val serverStatusMonitorId = interaction.getServerStatusMonitorIdParameter()
val hostName = interaction.getServerHostnameParameter()
val queryPort = interaction.getServerQueryPortParameter()
val status = interaction.getServerStatusMonitorStatusParameter()
val displayPlayerGearLevel = interaction.getDisplayPlayerGearLevelParameter()

val discordServerId = (interaction as GuildChatInputCommandInteraction).guildId
Expand All @@ -56,14 +58,17 @@ class UpdateServerCommand(
if (queryPort != null) {
serverStatusMonitorBuilder.queryPort = queryPort
}
if (status != null) {
serverStatusMonitorBuilder.status = status
}
if (displayPlayerGearLevel != null) {
serverStatusMonitorBuilder.displayPlayerGearLevel = displayPlayerGearLevel
}

serverStatusMonitorService.putServerStatusMonitor(serverStatusMonitorBuilder.build())

interaction.deferEphemeralResponse().respond {
content = "Updated server status monitor with id '${serverStatusMonitorId}'. It might take up to 1 minute for update to be visible."
content = "Updated server status monitor with id '${serverStatusMonitorId}'. It may take up to 1 minute before the update is visible."
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.darkatra.vrising.discord.migration

import de.darkatra.vrising.discord.ServerStatusMonitorBuilder

data class DatabaseMigration(
val isApplicable: (currentSchemaVersion: SemanticVersion) -> Boolean,
val action: (serverStatusMonitorBuilder: ServerStatusMonitorBuilder) -> Unit
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package de.darkatra.vrising.discord.migration

import de.darkatra.vrising.discord.ServerStatusMonitorService
import de.darkatra.vrising.discord.ServerStatusMonitorStatus
import org.dizitart.no2.Nitrite
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service

@Service
class DatabaseMigrationService(
database: Nitrite,
private val serverStatusMonitorService: ServerStatusMonitorService,
@Value("\${version}")
private val currentAppVersion: String
) {

private val logger = LoggerFactory.getLogger(javaClass)
private val repository = database.getRepository(Schema::class.java)

private val migrations: List<DatabaseMigration> = listOf(
DatabaseMigration(
isApplicable = { currentSchemaVersion -> currentSchemaVersion.major == 1 && currentSchemaVersion.minor <= 3 },
action = { serverStatusMonitorBuilder -> serverStatusMonitorBuilder.displayPlayerGearLevel = true }
),
DatabaseMigration(
isApplicable = { currentSchemaVersion -> currentSchemaVersion.major == 1 && currentSchemaVersion.minor <= 4 },
action = { serverStatusMonitorBuilder -> serverStatusMonitorBuilder.status = ServerStatusMonitorStatus.ACTIVE }
)
)

fun migrateToLatestVersion() {

// find the current version or default to V1.3.0 (the version before this feature was introduced)
val currentSchemaVersion = repository.find().toList()
.maxByOrNull(Schema::appVersion)
?.let(Schema::appVersionAsSemanticVersion)
?: SemanticVersion(major = 1, minor = 3, patch = 0)

val migrationsToPerform = migrations.filter { migration -> migration.isApplicable(currentSchemaVersion) }
if (migrationsToPerform.isNotEmpty()) {

val (major, minor, patch) = currentSchemaVersion
logger.info("Will migrate from V$major.$minor.$patch to V$currentAppVersion by performing ${migrationsToPerform.size} migrations.")

serverStatusMonitorService.getServerStatusMonitors().forEach { serverStatusMonitor ->
val serverStatusMonitorBuilder = serverStatusMonitor.builder()
migrationsToPerform.forEach { migration -> migration.action(serverStatusMonitorBuilder) }
serverStatusMonitorService.putServerStatusMonitor(serverStatusMonitorBuilder.build())
}

repository.insert(Schema("V$currentAppVersion"))
logger.info("Database migration from V$major.$minor.$patch to V$currentAppVersion was successful.")
} else {
logger.info("No migrations need to be performed.")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.darkatra.vrising.discord
package de.darkatra.vrising.discord.migration

import org.dizitart.no2.objects.Id

Expand All @@ -16,9 +16,3 @@ data class Schema(
)
}
}

data class SemanticVersion(
val major: Int,
val minor: Int,
val patch: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package de.darkatra.vrising.discord.migration

data class SemanticVersion(
val major: Int,
val minor: Int,
val patch: Int
)

0 comments on commit 20fc84f

Please sign in to comment.