summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-01-17 14:38:23 +0900
committerGitHub <noreply@github.com>2026-01-17 14:38:23 +0900
commitfac393d35a9ad0c937d67dbb8c3a6488e6133324 (patch)
tree1cd9da15977cb0e6685a3fa33336719948974343
parent37e697ebe130db167c452dd4f92c1f4cdf63af1f (diff)
parent6e358d6446d6ded6251ec153b9c6ccb0282909f1 (diff)
downloadLunaticChat-fac393d35a9ad0c937d67dbb8c3a6488e6133324.tar.gz
LunaticChat-fac393d35a9ad0c937d67dbb8c3a6488e6133324.tar.bz2
LunaticChat-fac393d35a9ad0c937d67dbb8c3a6488e6133324.zip
Merge pull request #41 from m1sk9/feat/check-update-logic
feat: Check for updates at startup
-rw-r--r--CHANGELOG.md4
-rw-r--r--build.gradle.kts2
-rw-r--r--docs/src/guide/configuration.md19
-rw-r--r--docs/src/guide/permissions.md8
-rw-r--r--engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt2
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/LunaticChat.kt57
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/common/UpdateChecker.kt80
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/ConfigManager.kt1
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/LunaticChatConfiguration.kt1
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt26
-rw-r--r--platform-paper/src/main/resources/config.yml3
-rw-r--r--platform-paper/src/main/resources/paper-plugin.yml2
12 files changed, 197 insertions, 8 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8e3bc0..07be4bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# LunaticChat Changelog
+### v0.4.0
+
+- Added an update checker that verifies whether updates are available when the server starts up.
+
### v0.3.1
#### Breaking Changes:
diff --git a/build.gradle.kts b/build.gradle.kts
index d2bd408..9430417 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -12,7 +12,7 @@ plugins {
allprojects {
group = "dev.m1sk9"
- version = "0.3.1"
+ version = "0.4.0"
repositories {
mavenCentral()
diff --git a/docs/src/guide/configuration.md b/docs/src/guide/configuration.md
index 14177b4..07458b3 100644
--- a/docs/src/guide/configuration.md
+++ b/docs/src/guide/configuration.md
@@ -16,11 +16,14 @@
# ----------------------------------------------
# If enabled, Activate LunaticChat's debug mode, which provides detailed logging for troubleshooting.
-debug: true
+debug: false
# Path to the YAML file storing player settings
userSettingsFilePath: "player-settings.yaml"
+# If enabled, LunaticChat will check for updates on startup.
+checkForUpdates: true
+
# ----------------------------------------------
# ----------- Features Settings ------------
# ----------------------------------------------
@@ -31,13 +34,18 @@ features:
enabled: true
japaneseConversion:
# If enabled, enables the conversion function from Roman letters to hiragana.
- enabled: true
+ enabled: false
cache:
+ # Specifies the maximum number of entries to store in the Romanization conversion cache.
maxEntries: 500
+ # Specify the interval (in seconds) for saving the Romanization conversion cache to disk.
saveIntervalSeconds: 300
+ # Specify the file path where the cache for Romanization conversion is saved.
filePath: "conversion_cache.json"
api:
+ # Specify the timeout duration (in milliseconds) for API requests to the Romanization conversion service.
timeout: 3000
+ # Specify the number of retry attempts for failed API requests to the Romanization conversion service.
retryAttempts: 2
# ----------------------------------------------
@@ -73,6 +81,13 @@ LunaticChat をデバッグモードで起動します.
LunaticChat がプレイヤーの設定を保存する YAML ファイルのパスを指定します.
+### `checkForUpdates`
+
+- Type: `boolean`
+- Default: `true`
+
+LunaticChat の起動時・権限を持ったプレイヤーがサーバに参加した際に,LunaticChat のアップデートを促すかどうか設定します.
+
## Features Settings
### `features.quickReplies.enabled`
diff --git a/docs/src/guide/permissions.md b/docs/src/guide/permissions.md
index eb37344..f5c27b9 100644
--- a/docs/src/guide/permissions.md
+++ b/docs/src/guide/permissions.md
@@ -12,6 +12,14 @@ LuckPerms を使用した設定方法に関する詳細は [LuckPerms Wiki](http
この権限を持つプレイヤーは他プレイヤーの [`/tell`](../reference/commands/tell.md) / [`/reply`](../reference/commands/reply.md) でのメッセージがブロードキャストされます.
+### `lunaticchat.noticeUpdate`
+
+- Default: `OP`
+
+LunaticChat のアップデート通知を受け取ります.
+
+[受け取るには `checkForUpdates` を有効にしておく](../guide/configuration.md#checkforupdates) 必要があります.
+
## `lunaticchat.command.*`
### `lunaticchat.command.tell`
diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt
index 5dfe8bf..052e2ee 100644
--- a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt
+++ b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt
@@ -10,4 +10,6 @@ sealed class LunaticChatPermissionNode(
object JapaneseToggle : LunaticChatPermissionNode("lunaticchat.command.jp")
object Spy : LunaticChatPermissionNode("lunaticchat.spy")
+
+ object NoticeUpdate : LunaticChatPermissionNode("lunaticchat.noticeUpdate")
}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/LunaticChat.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/LunaticChat.kt
index 330c13b..4bf2fc8 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/LunaticChat.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/LunaticChat.kt
@@ -6,6 +6,8 @@ import dev.m1sk9.lunaticChat.paper.command.impl.ReplyCommand
import dev.m1sk9.lunaticChat.paper.command.impl.RomajiConvertToggleCommand
import dev.m1sk9.lunaticChat.paper.command.impl.TellCommand
import dev.m1sk9.lunaticChat.paper.common.SpyPermissionManager
+import dev.m1sk9.lunaticChat.paper.common.UpdateCheckResult
+import dev.m1sk9.lunaticChat.paper.common.UpdateChecker
import dev.m1sk9.lunaticChat.paper.config.ConfigManager
import dev.m1sk9.lunaticChat.paper.config.LunaticChatConfiguration
import dev.m1sk9.lunaticChat.paper.converter.ConversionCache
@@ -17,8 +19,10 @@ import dev.m1sk9.lunaticChat.paper.settings.PlayerSettingsManager
import dev.m1sk9.lunaticChat.paper.settings.YamlPlayerSettingsStorage
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
+import kotlinx.coroutines.runBlocking
import org.bukkit.event.Listener
import org.bukkit.plugin.java.JavaPlugin
+import java.util.concurrent.atomic.AtomicBoolean
import kotlin.time.Duration.Companion.milliseconds
class LunaticChat :
@@ -27,9 +31,12 @@ class LunaticChat :
lateinit var directMessageHandler: DirectMessageHandler
private lateinit var commandRegistry: CommandRegistry
+ private var updateChecker: UpdateChecker? = null
private var romajiConverter: RomanjiConverter? = null
private var playerSettingsManager: PlayerSettingsManager? = null
+ private val updateAvailable = AtomicBoolean(false)
+
override fun onEnable() {
saveDefaultConfig()
val configuration = ConfigManager.loadConfiguration(config)
@@ -39,9 +46,11 @@ class LunaticChat :
logger.info("Debug: $configuration")
}
+ val httpClient = HttpClient(CIO)
+
// Initialize features
if (configuration.features.japaneseConversion.enabled) {
- initializeJapaneseConversionFeature(configuration)
+ initializeJapaneseConversionFeature(configuration, httpClient)
}
// Initialize handlers
@@ -55,6 +64,24 @@ class LunaticChat :
registerCommands(configuration)
registerEventListeners()
+ // Check for updates
+ if (configuration.checkForUpdates) {
+ updateChecker =
+ UpdateChecker(
+ currentVersion = pluginMeta.version,
+ logger = logger,
+ httpClient = httpClient,
+ )
+ server.scheduler.runTaskAsynchronously(
+ this,
+ Runnable {
+ runBlocking {
+ checkUpdates()
+ }
+ },
+ )
+ }
+
logger.info("LunaticChat enabled.")
}
@@ -71,7 +98,10 @@ class LunaticChat :
* - Romanji converter
* - Periodic cache saving task
*/
- private fun initializeJapaneseConversionFeature(configuration: LunaticChatConfiguration) {
+ private fun initializeJapaneseConversionFeature(
+ configuration: LunaticChatConfiguration,
+ httpClient: HttpClient,
+ ) {
// Initialize player settings
val settingsFile = dataFolder.resolve(configuration.userSettingsFilePath).toPath()
val storage =
@@ -99,7 +129,6 @@ class LunaticChat :
cache.loadFromDisk()
// Initialize Google IME API client
- val httpClient = HttpClient(CIO)
val apiClient =
GoogleIMEClient(
timeout = configuration.features.japaneseConversion.apiTimeout.milliseconds,
@@ -165,6 +194,26 @@ class LunaticChat :
*/
private fun registerEventListeners() {
server.pluginManager.registerEvents(SpyPermissionManager, this)
- server.pluginManager.registerEvents(PlayerPresenceListener(this), this)
+ server.pluginManager.registerEvents(PlayerPresenceListener(this, updateAvailable), this)
+ }
+
+ private suspend fun checkUpdates() {
+ val result = updateChecker?.checkForUpdates()
+ when (result) {
+ is UpdateCheckResult.ExistUpdate -> {
+ logger.info("A new version of LunaticChat is available!")
+ logger.info("You can download the latest build from GitHub or Modrinth.")
+ logger.info(" GitHub: https://github.com/m1sk9/LunaticChat/releases/latest")
+ logger.info(" Modrinth: https://modrinth.com/plugin/lunaticchat/version/latest")
+ updateAvailable.set(true)
+ }
+ is UpdateCheckResult.NotUpdate -> {
+ logger.info("LunaticChat is up to date.")
+ }
+ // Include failed case for completeness
+ else -> {
+ logger.warning("Failed to check for updates.")
+ }
+ }
}
}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/common/UpdateChecker.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/common/UpdateChecker.kt
new file mode 100644
index 0000000..49d6852
--- /dev/null
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/common/UpdateChecker.kt
@@ -0,0 +1,80 @@
+package dev.m1sk9.lunaticChat.paper.common
+
+import io.ktor.client.HttpClient
+import io.ktor.client.call.body
+import io.ktor.client.request.get
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.Json
+import java.util.logging.Logger
+
+@Serializable
+data class GitHubRelease(
+ @SerialName("tag_name")
+ val tagName: String,
+ @SerialName("name")
+ val name: String,
+ @SerialName("published_at")
+ val publishedAt: String,
+ @SerialName("html_url")
+ val htmlUrl: String,
+)
+
+class UpdateChecker(
+ private val currentVersion: String,
+ private val httpClient: HttpClient,
+ private val logger: Logger,
+) {
+ private val githubAPIURL = "https://api.github.com/repos/m1sk9/LunaticChat/releases/latest"
+ private val json = Json { ignoreUnknownKeys = true }
+
+ /**
+ * Check LunaticChat Updates
+ *
+ * @throws Exception Failed to check for updates
+ */
+ suspend fun checkForUpdates(): UpdateCheckResult {
+ return withContext(Dispatchers.IO) {
+ try {
+ val res = httpClient.get(githubAPIURL)
+ val release = json.decodeFromString<GitHubRelease>(res.body<String>())
+ val latestVersion = release.tagName.removePrefix("v")
+
+ if (!isNewer(latestVersion, currentVersion)) {
+ return@withContext UpdateCheckResult.NotUpdate
+ }
+
+ UpdateCheckResult.ExistUpdate
+ } catch (e: Exception) {
+ logger.warning("Failed to check for latest version of latest version: ${e.message}")
+ UpdateCheckResult.FailedUpdate
+ }
+ }
+ }
+
+ private fun isNewer(
+ latest: String,
+ current: String,
+ ): Boolean {
+ val latestParts = latest.split(".").map { it.toIntOrNull() ?: 0 }
+ val currentParts = current.split(".").map { it.toIntOrNull() ?: 0 }
+
+ for (i in 0 until maxOf(latestParts.size, currentParts.size)) {
+ val l = latestParts.getOrNull(i) ?: 0
+ val c = currentParts.getOrNull(i) ?: 0
+ if (l > c) return true
+ if (l < c) return false
+ }
+ return false
+ }
+}
+
+sealed class UpdateCheckResult {
+ object ExistUpdate : UpdateCheckResult()
+
+ object NotUpdate : UpdateCheckResult()
+
+ object FailedUpdate : UpdateCheckResult()
+}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/ConfigManager.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/ConfigManager.kt
index 8580504..5f3a1b0 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/ConfigManager.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/ConfigManager.kt
@@ -53,6 +53,7 @@ object ConfigManager {
)!!,
),
debug = configFile.getBoolean("debug", false),
+ checkForUpdates = configFile.getBoolean("checkForUpdates", false),
userSettingsFilePath =
configFile.getString(
"userSettingsFilePath",
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/LunaticChatConfiguration.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/LunaticChatConfiguration.kt
index e94687f..0dea2ac 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/LunaticChatConfiguration.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/LunaticChatConfiguration.kt
@@ -8,4 +8,5 @@ data class LunaticChatConfiguration(
val messageFormat: MessageFormatConfig,
val debug: Boolean = false,
val userSettingsFilePath: String = "player-settings.yaml",
+ val checkForUpdates: Boolean = true,
)
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt
index 623a544..d0873a6 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerPresenceListener.kt
@@ -1,16 +1,40 @@
package dev.m1sk9.lunaticChat.paper.listener
+import dev.m1sk9.lunaticChat.engine.permission.LunaticChatPermissionNode
import dev.m1sk9.lunaticChat.paper.LunaticChat
+import dev.m1sk9.lunaticChat.paper.common.hasAnyPermission
+import net.kyori.adventure.text.Component
+import net.kyori.adventure.text.event.ClickEvent
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
+import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent
+import java.util.concurrent.atomic.AtomicBoolean
class PlayerPresenceListener(
private val lunaticChat: LunaticChat,
+ private val updateCheckerFlag: AtomicBoolean,
) : Listener {
@EventHandler(ignoreCancelled = true)
+ fun onJoin(event: PlayerJoinEvent) {
+ val player = event.player
+ if (!updateCheckerFlag.get() || !player.hasAnyPermission { +LunaticChatPermissionNode.NoticeUpdate }) return
+
+ player.sendMessage {
+ Component
+ .text(
+ listOf(
+ "§6[§eLunaticChat§6] §aA new update is available!",
+ "§aYou can download the latest build from §bGitHub §aor §bModrinth.",
+ ).joinToString("\n"),
+ ).clickEvent(
+ ClickEvent.openUrl("https://modrinth.com/plugin/lunaticchat/version/latest"),
+ )
+ }
+ }
+
+ @EventHandler(ignoreCancelled = true)
fun onQuit(event: PlayerQuitEvent) {
lunaticChat.directMessageHandler.clearPlayer(event.player)
- // Settings remain in memory, no unloading needed
}
}
diff --git a/platform-paper/src/main/resources/config.yml b/platform-paper/src/main/resources/config.yml
index f1ebe64..e6a372d 100644
--- a/platform-paper/src/main/resources/config.yml
+++ b/platform-paper/src/main/resources/config.yml
@@ -18,6 +18,9 @@ debug: false
# Path to the YAML file storing player settings
userSettingsFilePath: "player-settings.yaml"
+# If enabled, LunaticChat will check for updates on startup.
+checkForUpdates: true
+
# ----------------------------------------------
# ----------- Features Settings ------------
# ----------------------------------------------
diff --git a/platform-paper/src/main/resources/paper-plugin.yml b/platform-paper/src/main/resources/paper-plugin.yml
index 97b6358..3121051 100644
--- a/platform-paper/src/main/resources/paper-plugin.yml
+++ b/platform-paper/src/main/resources/paper-plugin.yml
@@ -18,3 +18,5 @@ permissions:
lunaticchat.spy:
default: op
+ lunaticchat.noticeUpdate:
+ default: op