summaryrefslogtreecommitdiff
path: root/platform-paper/src/main/kotlin
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-01-24 01:01:07 +0900
committerSho Sakuma <me@m1sk9.dev>2026-01-24 01:01:15 +0900
commitfc5caa61437e49f275f8d3075bbb460430f4f19b (patch)
treeee12e555421235ce3010e0199ed23fab89f4086d /platform-paper/src/main/kotlin
parenta32fda5e0cf79c4e443290d149ddd3f76a12e8b4 (diff)
downloadLunaticChat-fc5caa61437e49f275f8d3075bbb460430f4f19b.tar.gz
LunaticChat-fc5caa61437e49f275f8d3075bbb460430f4f19b.tar.bz2
LunaticChat-fc5caa61437e49f275f8d3075bbb460430f4f19b.zip
feat: Consolidate configuration commands into `/lc settings`
Diffstat (limited to 'platform-paper/src/main/kotlin')
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/LunaticChat.kt29
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/core/LunaticCommand.kt22
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/DirectMessageNoticeToggleCommand.kt94
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/RomajiConvertToggleCommand.kt94
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lc/LunaticChatCommand.kt39
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lc/SettingsCommand.kt105
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingHandler.kt36
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingHandlerRegistry.kt45
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingKey.kt35
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/handler/DirectMessageNoticeSettingHandler.kt53
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/handler/JapaneseConversionSettingHandler.kt53
11 files changed, 392 insertions, 213 deletions
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 7dcc84e..c4cf9ec 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
@@ -3,10 +3,8 @@ package dev.m1sk9.lunaticChat.paper
import dev.m1sk9.lunaticChat.engine.converter.GoogleIMEClient
import dev.m1sk9.lunaticChat.paper.command.core.CommandRegistry
import dev.m1sk9.lunaticChat.paper.command.handler.DirectMessageHandler
-import dev.m1sk9.lunaticChat.paper.command.impl.DirectMessageNoticeToggleCommand
-import dev.m1sk9.lunaticChat.paper.command.impl.LunaticChatCommand
+import dev.m1sk9.lunaticChat.paper.command.impl.lc.LunaticChatCommand
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.command.setting.SettingHandlerRegistry
import dev.m1sk9.lunaticChat.paper.command.setting.handler.DirectMessageNoticeSettingHandler
@@ -191,30 +189,22 @@ class LunaticChat :
private fun registerCommands(configuration: LunaticChatConfiguration) {
commandRegistry = CommandRegistry(this)
- // Initialize setting handler registry for /lc setting command
val settingHandlerRegistry = SettingHandlerRegistry()
-
- // Register setting handlers
settingHandlerRegistry.register(
DirectMessageNoticeSettingHandler(playerSettingsManager!!, languageManager),
)
+ commandRegistry.registerAll(
+ TellCommand(this, directMessageHandler, languageManager),
+ LunaticChatCommand(this, settingHandlerRegistry, languageManager),
+ )
+
if (configuration.features.japaneseConversion.enabled) {
settingHandlerRegistry.register(
JapaneseConversionSettingHandler(playerSettingsManager!!, languageManager),
)
}
- // Register new /lc command with setting subcommand
- commandRegistry.register(
- LunaticChatCommand(this, settingHandlerRegistry, languageManager),
- )
-
- commandRegistry.registerAll(
- TellCommand(this, directMessageHandler, languageManager),
- DirectMessageNoticeToggleCommand(this, playerSettingsManager!!, languageManager),
- )
-
// Register /reply command if quick replies are enabled
if (configuration.features.quickRepliesEnabled.enabled) {
commandRegistry.registerAll(
@@ -222,13 +212,6 @@ class LunaticChat :
)
}
- // Register /jp command if Japanese conversion is enabled
- if (configuration.features.japaneseConversion.enabled) {
- commandRegistry.registerAll(
- RomajiConvertToggleCommand(this, playerSettingsManager!!, languageManager),
- )
- }
-
commandRegistry.initialize()
}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/core/LunaticCommand.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/core/LunaticCommand.kt
index d8ca412..b11fd1a 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/core/LunaticCommand.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/core/LunaticCommand.kt
@@ -7,8 +7,8 @@ import dev.m1sk9.lunaticChat.paper.command.annotation.Command
import dev.m1sk9.lunaticChat.paper.command.annotation.Permission
import dev.m1sk9.lunaticChat.paper.command.annotation.PlayerOnly
import dev.m1sk9.lunaticChat.paper.i18n.MessageFormatter
-import dev.m1sk9.lunaticChat.paper.i18n.MessageKey
import io.papermc.paper.command.brigadier.CommandSourceStack
+import io.papermc.paper.command.brigadier.Commands
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
@@ -30,6 +30,10 @@ abstract class LunaticCommand(
this::class.annotations.filterIsInstance<Permission>().firstOrNull()
}
+ private val deprecatedAnnotation: Deprecated? by lazy {
+ this::class.annotations.filterIsInstance<Deprecated>().firstOrNull()
+ }
+
private val isPlayerOnly: Boolean by lazy {
this::class.annotations.any { it is PlayerOnly }
}
@@ -59,6 +63,20 @@ abstract class LunaticCommand(
* Called by CommandRegistry during registration.
*/
fun buildWithChecks(): LiteralArgumentBuilder<CommandSourceStack> {
+ // If command is deprecated, replace with error message handler
+ deprecatedAnnotation?.let { deprecated ->
+ return Commands
+ .literal(name)
+ .executes { ctx ->
+ val context = wrapContext(ctx)
+ val result =
+ CommandResult.Failure(
+ MessageFormatter.formatError(deprecated.message),
+ )
+ handleResult(context, result)
+ }
+ }
+
var builder = buildCommand()
permission?.let { perm ->
builder =
@@ -78,7 +96,7 @@ abstract class LunaticCommand(
if (isPlayerOnly && !ctx.isPlayer) {
return CommandResult.Failure(
MessageFormatter.formatError(
- plugin.languageManager.getMessage(MessageKey.PlayerOnlyCommand),
+ plugin.languageManager.getMessage("playerOnlyCommand"),
),
)
}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/DirectMessageNoticeToggleCommand.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/DirectMessageNoticeToggleCommand.kt
deleted file mode 100644
index e838ee0..0000000
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/DirectMessageNoticeToggleCommand.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-package dev.m1sk9.lunaticChat.paper.command.impl
-
-import com.mojang.brigadier.builder.LiteralArgumentBuilder
-import dev.m1sk9.lunaticChat.engine.command.CommandResult
-import dev.m1sk9.lunaticChat.engine.permission.LunaticChatPermissionNode
-import dev.m1sk9.lunaticChat.paper.LunaticChat
-import dev.m1sk9.lunaticChat.paper.command.annotation.Command
-import dev.m1sk9.lunaticChat.paper.command.annotation.Permission
-import dev.m1sk9.lunaticChat.paper.command.annotation.PlayerOnly
-import dev.m1sk9.lunaticChat.paper.command.core.CommandContext
-import dev.m1sk9.lunaticChat.paper.command.core.LunaticCommand
-import dev.m1sk9.lunaticChat.paper.i18n.LanguageManager
-import dev.m1sk9.lunaticChat.paper.i18n.MessageFormatter
-import dev.m1sk9.lunaticChat.paper.i18n.MessageKey
-import dev.m1sk9.lunaticChat.paper.settings.PlayerSettingsManager
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import io.papermc.paper.command.brigadier.Commands
-
-@Command(
- name = "notice",
- aliases = ["dmnotice"],
- description = "",
-)
-@Permission(LunaticChatPermissionNode.NoticeToggle::class)
-@PlayerOnly
-class DirectMessageNoticeToggleCommand(
- plugin: LunaticChat,
- private val settingsManager: PlayerSettingsManager,
- private val languageManager: LanguageManager,
-) : LunaticCommand(plugin) {
- override val description: String
- get() = languageManager.getMessage(MessageKey.CommandDescriptionNotice)
-
- override fun buildCommand(): LiteralArgumentBuilder<CommandSourceStack> =
- Commands
- .literal(name)
- .then(
- Commands
- .literal("on")
- .executes { ctx ->
- val context = wrapContext(ctx)
- checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
- val result = execute(context, true)
- handleResult(context, result)
- },
- ).then(
- Commands
- .literal("off")
- .executes { ctx ->
- val context = wrapContext(ctx)
- checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
- val result = execute(context, false)
- handleResult(context, result)
- },
- ).executes { ctx ->
- val context = wrapContext(ctx)
- checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
- val result = showStatus(context)
- handleResult(context, result)
- }
-
- private fun execute(
- ctx: CommandContext,
- enable: Boolean,
- ): CommandResult {
- val player = ctx.requirePlayer()
- val currentSettings = settingsManager.getSettings(player.uniqueId)
- val updatedSettings = currentSettings.copy(directMessageNotificationEnabled = enable)
- settingsManager.updateSettings(updatedSettings)
-
- val toggleText = languageManager.getToggleText(enable)
- val message =
- MessageFormatter.formatSuccess(
- languageManager.getMessage(MessageKey.DirectMessageNoticeToggle(toggleText)),
- )
-
- player.sendMessage(message)
- return CommandResult.Success
- }
-
- private fun showStatus(ctx: CommandContext): CommandResult {
- val player = ctx.requirePlayer()
- val settings = settingsManager.getSettings(player.uniqueId)
-
- val toggleText = languageManager.getToggleText(settings.directMessageNotificationEnabled)
- val message =
- MessageFormatter.format(
- languageManager.getMessage(MessageKey.DirectMessageNoticeStatus(toggleText)),
- )
-
- player.sendMessage(message)
- return CommandResult.Success
- }
-}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/RomajiConvertToggleCommand.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/RomajiConvertToggleCommand.kt
deleted file mode 100644
index b25667d..0000000
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/RomajiConvertToggleCommand.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-package dev.m1sk9.lunaticChat.paper.command.impl
-
-import com.mojang.brigadier.builder.LiteralArgumentBuilder
-import dev.m1sk9.lunaticChat.engine.command.CommandResult
-import dev.m1sk9.lunaticChat.engine.permission.LunaticChatPermissionNode
-import dev.m1sk9.lunaticChat.paper.LunaticChat
-import dev.m1sk9.lunaticChat.paper.command.annotation.Command
-import dev.m1sk9.lunaticChat.paper.command.annotation.Permission
-import dev.m1sk9.lunaticChat.paper.command.annotation.PlayerOnly
-import dev.m1sk9.lunaticChat.paper.command.core.CommandContext
-import dev.m1sk9.lunaticChat.paper.command.core.LunaticCommand
-import dev.m1sk9.lunaticChat.paper.i18n.LanguageManager
-import dev.m1sk9.lunaticChat.paper.i18n.MessageFormatter
-import dev.m1sk9.lunaticChat.paper.i18n.MessageKey
-import dev.m1sk9.lunaticChat.paper.settings.PlayerSettingsManager
-import io.papermc.paper.command.brigadier.CommandSourceStack
-import io.papermc.paper.command.brigadier.Commands
-
-@Command(
- name = "jp",
- aliases = [],
- description = "",
-)
-@Permission(LunaticChatPermissionNode.JapaneseToggle::class)
-@PlayerOnly
-class RomajiConvertToggleCommand(
- plugin: LunaticChat,
- private val settingsManager: PlayerSettingsManager,
- private val languageManager: LanguageManager,
-) : LunaticCommand(plugin) {
- override val description: String
- get() = languageManager.getMessage(MessageKey.CommandDescriptionJp)
-
- override fun buildCommand(): LiteralArgumentBuilder<CommandSourceStack> =
- Commands
- .literal(name)
- .then(
- Commands
- .literal("on")
- .executes { ctx ->
- val context = wrapContext(ctx)
- checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
- val result = execute(context, true)
- handleResult(context, result)
- },
- ).then(
- Commands
- .literal("off")
- .executes { ctx ->
- val context = wrapContext(ctx)
- checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
- val result = execute(context, false)
- handleResult(context, result)
- },
- ).executes { ctx ->
- val context = wrapContext(ctx)
- checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
- val result = showStatus(context)
- handleResult(context, result)
- }
-
- private fun execute(
- ctx: CommandContext,
- enable: Boolean,
- ): CommandResult {
- val player = ctx.requirePlayer()
- val currentSettings = settingsManager.getSettings(player.uniqueId)
- val updatedSettings = currentSettings.copy(japaneseConversionEnabled = enable)
- settingsManager.updateSettings(updatedSettings)
-
- val toggleText = languageManager.getToggleText(enable)
- val message =
- MessageFormatter.formatSuccess(
- languageManager.getMessage(MessageKey.RomajiConversionToggle(toggleText)),
- )
-
- player.sendMessage(message)
- return CommandResult.Success
- }
-
- private fun showStatus(ctx: CommandContext): CommandResult {
- val player = ctx.requirePlayer()
- val settings = settingsManager.getSettings(player.uniqueId)
-
- val toggleText = languageManager.getToggleText(settings.japaneseConversionEnabled)
- val message =
- MessageFormatter.format(
- languageManager.getMessage(MessageKey.RomajiConversionStatus(toggleText)),
- )
-
- player.sendMessage(message)
- return CommandResult.Success
- }
-}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lc/LunaticChatCommand.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lc/LunaticChatCommand.kt
new file mode 100644
index 0000000..ba3adf4
--- /dev/null
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lc/LunaticChatCommand.kt
@@ -0,0 +1,39 @@
+package dev.m1sk9.lunaticChat.paper.command.impl.lc
+
+import com.mojang.brigadier.builder.LiteralArgumentBuilder
+import dev.m1sk9.lunaticChat.paper.LunaticChat
+import dev.m1sk9.lunaticChat.paper.command.annotation.Command
+import dev.m1sk9.lunaticChat.paper.command.core.LunaticCommand
+import dev.m1sk9.lunaticChat.paper.command.setting.SettingHandlerRegistry
+import dev.m1sk9.lunaticChat.paper.i18n.LanguageManager
+import io.papermc.paper.command.brigadier.CommandSourceStack
+import io.papermc.paper.command.brigadier.Commands
+
+/**
+ * Main LunaticChat command (/lc).
+ * This command serves as the entry point for all LunaticChat subcommands.
+ */
+@Command(
+ name = "lc",
+ aliases = ["lunaticchat"],
+ description = "",
+)
+class LunaticChatCommand(
+ plugin: LunaticChat,
+ private val settingHandlerRegistry: SettingHandlerRegistry,
+ private val languageManager: LanguageManager,
+) : LunaticCommand(plugin) {
+ override val description: String
+ get() = languageManager.getMessage("commandDescription.lc")
+
+ override fun buildCommand(): LiteralArgumentBuilder<CommandSourceStack> =
+ Commands
+ .literal(name)
+ .then(
+ SettingsCommand(
+ plugin,
+ settingHandlerRegistry,
+ languageManager,
+ ).build(),
+ )
+}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lc/SettingsCommand.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lc/SettingsCommand.kt
new file mode 100644
index 0000000..b6a79b3
--- /dev/null
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/impl/lc/SettingsCommand.kt
@@ -0,0 +1,105 @@
+package dev.m1sk9.lunaticChat.paper.command.impl.lc
+
+import com.mojang.brigadier.builder.LiteralArgumentBuilder
+import dev.m1sk9.lunaticChat.engine.command.CommandResult
+import dev.m1sk9.lunaticChat.paper.LunaticChat
+import dev.m1sk9.lunaticChat.paper.command.annotation.PlayerOnly
+import dev.m1sk9.lunaticChat.paper.command.core.CommandContext
+import dev.m1sk9.lunaticChat.paper.command.core.LunaticCommand
+import dev.m1sk9.lunaticChat.paper.command.setting.SettingHandlerRegistry
+import dev.m1sk9.lunaticChat.paper.command.setting.SettingKey
+import dev.m1sk9.lunaticChat.paper.i18n.LanguageManager
+import dev.m1sk9.lunaticChat.paper.i18n.MessageFormatter
+import io.papermc.paper.command.brigadier.CommandSourceStack
+import io.papermc.paper.command.brigadier.Commands
+
+/**
+ * Setting subcommand for /lc command.
+ * Handles /lc setting <key> [on|off]
+ *
+ * This subcommand is extensible - new settings can be added by:
+ * 1. Adding a new SettingKey
+ * 2. Creating a SettingHandler implementation
+ * 3. Registering the handler in SettingHandlerRegistry
+ */
+@PlayerOnly
+class SettingsCommand(
+ plugin: LunaticChat,
+ private val settingHandlerRegistry: SettingHandlerRegistry,
+ private val languageManager: LanguageManager,
+) : LunaticCommand(plugin) {
+ /**
+ * Builds the setting subcommand structure.
+ * For each registered setting key, creates:
+ * - /lc setting <key> on
+ * - /lc setting <key> off
+ * - /lc setting <key> (shows status)
+ */
+ fun build(): LiteralArgumentBuilder<CommandSourceStack> {
+ val settingCommand = Commands.literal("settings")
+
+ for (settingKey in SettingKey.Companion.values()) {
+ val handler = settingHandlerRegistry.getHandler(settingKey)
+ if (handler == null) {
+ plugin.logger.warning("No handler registered for setting key: ${settingKey.key}")
+ continue
+ }
+
+ val keyCommand =
+ Commands
+ .literal(settingKey.key)
+ .then(
+ Commands
+ .literal("on")
+ .executes { ctx ->
+ val context = wrapContext(ctx)
+ checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
+ val result = handler.execute(context, true)
+ handleResult(context, result)
+ },
+ ).then(
+ Commands
+ .literal("off")
+ .executes { ctx ->
+ val context = wrapContext(ctx)
+ checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
+ val result = handler.execute(context, false)
+ handleResult(context, result)
+ },
+ ).executes { ctx ->
+ val context = wrapContext(ctx)
+ checkPlayerOnly(context)?.let { return@executes handleResult(context, it) }
+ val result = handler.showStatus(context)
+ handleResult(context, result)
+ }
+
+ settingCommand.then(keyCommand)
+ }
+
+ settingCommand.executes { ctx ->
+ val context = wrapContext(ctx)
+ val result = showHelp(context)
+ handleResult(context, result)
+ }
+
+ return settingCommand
+ }
+
+ /**
+ * Shows help message for the setting subcommand.
+ * Lists all available setting keys.
+ */
+ private fun showHelp(ctx: CommandContext): CommandResult {
+ val availableKeys = settingHandlerRegistry.getAvailableKeys()
+ val helpMessage =
+ MessageFormatter.format(languageManager.getMessage("settingsAvailableValues", mapOf("values" to availableKeys.joinToString(", "))))
+
+ ctx.reply(helpMessage)
+ return CommandResult.Success
+ }
+
+ override fun buildCommand(): LiteralArgumentBuilder<CommandSourceStack> =
+ throw UnsupportedOperationException(
+ "SettingsSubcommand should use build() method instead of buildCommand()",
+ )
+}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingHandler.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingHandler.kt
new file mode 100644
index 0000000..c36257c
--- /dev/null
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingHandler.kt
@@ -0,0 +1,36 @@
+package dev.m1sk9.lunaticChat.paper.command.setting
+
+import dev.m1sk9.lunaticChat.engine.command.CommandResult
+import dev.m1sk9.lunaticChat.paper.command.core.CommandContext
+
+/**
+ * Interface for handling individual setting operations.
+ * Each setting (japanese, notice, etc.) implements this interface to provide
+ * its own logic for getting, setting, and displaying status.
+ */
+interface SettingHandler {
+ /**
+ * The setting key this handler manages.
+ */
+ val key: SettingKey
+
+ /**
+ * Enables or disables the setting for a player.
+ *
+ * @param ctx The command context containing player information
+ * @param enable True to enable, false to disable
+ * @return Command result indicating success or failure
+ */
+ fun execute(
+ ctx: CommandContext,
+ enable: Boolean,
+ ): CommandResult
+
+ /**
+ * Shows the current status of the setting for a player.
+ *
+ * @param ctx The command context containing player information
+ * @return Command result indicating success or failure
+ */
+ fun showStatus(ctx: CommandContext): CommandResult
+}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingHandlerRegistry.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingHandlerRegistry.kt
new file mode 100644
index 0000000..13c3a77
--- /dev/null
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingHandlerRegistry.kt
@@ -0,0 +1,45 @@
+package dev.m1sk9.lunaticChat.paper.command.setting
+
+/**
+ * Registry for all setting handlers.
+ * Provides centralized access to setting handlers and ensures type safety.
+ */
+class SettingHandlerRegistry {
+ private val handlers = mutableMapOf<SettingKey, SettingHandler>()
+
+ /**
+ * Registers a setting handler.
+ *
+ * @param handler The handler to register
+ * @return This registry for method chaining
+ */
+ fun register(handler: SettingHandler): SettingHandlerRegistry {
+ handlers[handler.key] = handler
+ return this
+ }
+
+ /**
+ * Registers multiple setting handlers at once.
+ *
+ * @param handlers The handlers to register
+ * @return This registry for method chaining
+ */
+ fun registerAll(vararg handlers: SettingHandler): SettingHandlerRegistry {
+ handlers.forEach { register(it) }
+ return this
+ }
+
+ /**
+ * Retrieves a handler for a specific setting key.
+ *
+ * @param key The setting key to look up
+ * @return The handler if found, null otherwise
+ */
+ fun getHandler(key: SettingKey): SettingHandler? = handlers[key]
+
+ /**
+ * Returns all registered setting keys.
+ * Useful for tab completion.
+ */
+ fun getAvailableKeys(): List<String> = handlers.keys.map { it.key }
+}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingKey.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingKey.kt
new file mode 100644
index 0000000..26a1f06
--- /dev/null
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/SettingKey.kt
@@ -0,0 +1,35 @@
+package dev.m1sk9.lunaticChat.paper.command.setting
+
+/**
+ * Sealed class representing all available setting keys.
+ * Each setting key has a unique identifier used in command arguments.
+ */
+sealed class SettingKey(
+ val key: String,
+) {
+ /**
+ * Japanese romaji conversion setting
+ * Command: /lc setting japanese <on|off>
+ */
+ data object Japanese : SettingKey("japanese")
+
+ /**
+ * Direct message notification setting
+ * Command: /lc setting notice <on|off>
+ */
+ data object Notice : SettingKey("notice")
+
+ companion object {
+ /**
+ * Returns all available setting keys.
+ */
+ fun values(): List<SettingKey> = listOf(Japanese, Notice)
+
+ /**
+ * Finds a setting key by its string representation.
+ * @param key The key string to search for
+ * @return The matching SettingKey or null if not found
+ */
+ fun fromString(key: String): SettingKey? = values().find { it.key.equals(key, ignoreCase = true) }
+ }
+}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/handler/DirectMessageNoticeSettingHandler.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/handler/DirectMessageNoticeSettingHandler.kt
new file mode 100644
index 0000000..90ff0ad
--- /dev/null
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/handler/DirectMessageNoticeSettingHandler.kt
@@ -0,0 +1,53 @@
+package dev.m1sk9.lunaticChat.paper.command.setting.handler
+
+import dev.m1sk9.lunaticChat.engine.command.CommandResult
+import dev.m1sk9.lunaticChat.paper.command.core.CommandContext
+import dev.m1sk9.lunaticChat.paper.command.setting.SettingHandler
+import dev.m1sk9.lunaticChat.paper.command.setting.SettingKey
+import dev.m1sk9.lunaticChat.paper.i18n.LanguageManager
+import dev.m1sk9.lunaticChat.paper.i18n.MessageFormatter
+import dev.m1sk9.lunaticChat.paper.settings.PlayerSettingsManager
+
+/**
+ * Handles the direct message notification setting.
+ * Manages enabling/disabling DM notifications for players.
+ */
+class DirectMessageNoticeSettingHandler(
+ private val settingsManager: PlayerSettingsManager,
+ private val languageManager: LanguageManager,
+) : SettingHandler {
+ override val key: SettingKey = SettingKey.Notice
+
+ override fun execute(
+ ctx: CommandContext,
+ enable: Boolean,
+ ): CommandResult {
+ val player = ctx.requirePlayer()
+ val currentSettings = settingsManager.getSettings(player.uniqueId)
+ val updatedSettings = currentSettings.copy(directMessageNotificationEnabled = enable)
+ settingsManager.updateSettings(updatedSettings)
+
+ val toggleText = languageManager.getToggleText(enable)
+ val message =
+ MessageFormatter.formatSuccess(
+ languageManager.getMessage("directMessageNoticeToggle", mapOf("toggle" to toggleText)),
+ )
+
+ player.sendMessage(message)
+ return CommandResult.Success
+ }
+
+ override fun showStatus(ctx: CommandContext): CommandResult {
+ val player = ctx.requirePlayer()
+ val settings = settingsManager.getSettings(player.uniqueId)
+
+ val toggleText = languageManager.getToggleText(settings.directMessageNotificationEnabled)
+ val message =
+ MessageFormatter.format(
+ languageManager.getMessage("directMessageNoticeStatus", mapOf("toggle" to toggleText)),
+ )
+
+ player.sendMessage(message)
+ return CommandResult.Success
+ }
+}
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/handler/JapaneseConversionSettingHandler.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/handler/JapaneseConversionSettingHandler.kt
new file mode 100644
index 0000000..39cbc0d
--- /dev/null
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/command/setting/handler/JapaneseConversionSettingHandler.kt
@@ -0,0 +1,53 @@
+package dev.m1sk9.lunaticChat.paper.command.setting.handler
+
+import dev.m1sk9.lunaticChat.engine.command.CommandResult
+import dev.m1sk9.lunaticChat.paper.command.core.CommandContext
+import dev.m1sk9.lunaticChat.paper.command.setting.SettingHandler
+import dev.m1sk9.lunaticChat.paper.command.setting.SettingKey
+import dev.m1sk9.lunaticChat.paper.i18n.LanguageManager
+import dev.m1sk9.lunaticChat.paper.i18n.MessageFormatter
+import dev.m1sk9.lunaticChat.paper.settings.PlayerSettingsManager
+
+/**
+ * Handles the Japanese romaji conversion setting.
+ * Manages enabling/disabling Japanese conversion for players.
+ */
+class JapaneseConversionSettingHandler(
+ private val settingsManager: PlayerSettingsManager,
+ private val languageManager: LanguageManager,
+) : SettingHandler {
+ override val key: SettingKey = SettingKey.Japanese
+
+ override fun execute(
+ ctx: CommandContext,
+ enable: Boolean,
+ ): CommandResult {
+ val player = ctx.requirePlayer()
+ val currentSettings = settingsManager.getSettings(player.uniqueId)
+ val updatedSettings = currentSettings.copy(japaneseConversionEnabled = enable)
+ settingsManager.updateSettings(updatedSettings)
+
+ val toggleText = languageManager.getToggleText(enable)
+ val message =
+ MessageFormatter.formatSuccess(
+ languageManager.getMessage("romajiConversionToggle", mapOf("toggle" to toggleText)),
+ )
+
+ player.sendMessage(message)
+ return CommandResult.Success
+ }
+
+ override fun showStatus(ctx: CommandContext): CommandResult {
+ val player = ctx.requirePlayer()
+ val settings = settingsManager.getSettings(player.uniqueId)
+
+ val toggleText = languageManager.getToggleText(settings.japaneseConversionEnabled)
+ val message =
+ MessageFormatter.format(
+ languageManager.getMessage("romajiConversionStatus", mapOf("toggle" to toggleText)),
+ )
+
+ player.sendMessage(message)
+ return CommandResult.Success
+ }
+}