From cdb03c1e628004820b0d2b70682f52f4f97c4663 Mon Sep 17 00:00:00 2001 From: Sho Sakuma Date: Sat, 31 Jan 2026 17:50:11 +0900 Subject: feat: Add channel logging --- .../engine/chat/channel/ChannelMessageLogEntry.kt | 51 ++++++ .../m1sk9/lunaticChat/paper/ServiceInitializer.kt | 45 +++++ .../paper/chat/channel/ChannelMessageLogger.kt | 191 +++++++++++++++++++++ .../paper/chat/handler/ChannelMessageHandler.kt | 16 ++ .../paper/config/key/ChannelChatFeatureConfig.kt | 4 + .../config/key/ChannelMessageLoggingConfig.kt | 20 +++ platform-paper/src/main/resources/config.yml | 8 + 7 files changed, 335 insertions(+) create mode 100644 engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/chat/channel/ChannelMessageLogEntry.kt create mode 100644 platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMessageLogger.kt create mode 100644 platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/key/ChannelMessageLoggingConfig.kt diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/chat/channel/ChannelMessageLogEntry.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/chat/channel/ChannelMessageLogEntry.kt new file mode 100644 index 0000000..7cd4eb6 --- /dev/null +++ b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/chat/channel/ChannelMessageLogEntry.kt @@ -0,0 +1,51 @@ +package dev.m1sk9.lunaticChat.engine.chat.channel + +import kotlinx.serialization.Serializable +import java.time.Instant +import java.util.UUID + +/** + * Represents a single channel message log entry in NDJSON format. + * + * This data class is designed for append-only logging to daily rotated files, + * compatible with Grafana Loki and other log aggregation systems. + * + * @property timestamp ISO-8601 formatted UTC timestamp of when the message was sent + * @property playerId UUID of the player who sent the message + * @property playerName Display name of the player + * @property channelId Unique identifier of the channel + * @property message The chat message content + */ +@Serializable +data class ChannelMessageLogEntry( + val timestamp: String, + val playerId: String, + val playerName: String, + val channelId: String, + val message: String, +) { + companion object { + /** + * Creates a new log entry with the current timestamp. + * + * @param playerId UUID of the player + * @param playerName Display name of the player + * @param channelId Channel identifier + * @param message Message content + * @return New ChannelMessageLogEntry instance + */ + fun create( + playerId: UUID, + playerName: String, + channelId: String, + message: String, + ): ChannelMessageLogEntry = + ChannelMessageLogEntry( + timestamp = Instant.now().toString(), + playerId = playerId.toString(), + playerName = playerName, + channelId = channelId, + message = message, + ) + } +} diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/ServiceInitializer.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/ServiceInitializer.kt index daba3dd..e6ea864 100644 --- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/ServiceInitializer.kt +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/ServiceInitializer.kt @@ -5,6 +5,7 @@ import dev.m1sk9.lunaticChat.paper.chat.ChatModeManager import dev.m1sk9.lunaticChat.paper.chat.ChatModeStorage import dev.m1sk9.lunaticChat.paper.chat.channel.ChannelManager import dev.m1sk9.lunaticChat.paper.chat.channel.ChannelMembershipManager +import dev.m1sk9.lunaticChat.paper.chat.channel.ChannelMessageLogger import dev.m1sk9.lunaticChat.paper.chat.channel.ChannelStorage import dev.m1sk9.lunaticChat.paper.chat.handler.ChannelMessageHandler import dev.m1sk9.lunaticChat.paper.chat.handler.ChannelNotificationHandler @@ -49,6 +50,7 @@ class ServiceInitializer( private var chatModeManager: ChatModeManager? = null private var channelMessageHandler: ChannelMessageHandler? = null private var channelNotificationHandler: ChannelNotificationHandler? = null + private var channelMessageLogger: ChannelMessageLogger? = null /** * Initializes all services in dependency order. @@ -227,6 +229,27 @@ class ServiceInitializer( chatMode.initialize() chatModeManager = chatMode + // Initialize channel message logger if enabled + val messageLogger = + if (configuration.features.channelChat.messageLogging.enabled) { + val logsDir = plugin.dataFolder.resolve("logs/channelchat").toPath() + ChannelMessageLogger( + logsDirectory = logsDir, + plugin = plugin, + logger = + io.ktor.util.logging + .KtorSimpleLogger("ChannelMessageLogger"), + maxFileSizeBytes = configuration.features.channelChat.messageLogging.maxFileSizeMB * 1024L * 1024L, + ).also { + channelMessageLogger = it + logger.info( + "Channel message logging enabled (retention: ${configuration.features.channelChat.messageLogging.retentionDays} days)", + ) + } + } else { + null + } + val messageHandler = ChannelMessageHandler( configuration = configuration, @@ -234,6 +257,7 @@ class ServiceInitializer( channelManager = manager, romanjiConverter = romajiConverter, languageManager = languageManager, + messageLogger = messageLogger, logger = io.ktor.util.logging .KtorSimpleLogger("ChannelMessageHandler"), @@ -275,6 +299,26 @@ class ServiceInitializer( saveInterval, ) } + + // Schedule cleanup of old channel message logs + if (configuration.features.channelChat.messageLogging.enabled && + configuration.features.channelChat.messageLogging.retentionDays > 0 && + channelMessageLogger != null + ) { + val cleanupInterval = 24 * 60 * 60 * 20L // 24 hours in ticks + val initialDelay = 5 * 60 * 20L // 5 minutes after startup + + plugin.server.scheduler.runTaskTimerAsynchronously( + plugin, + Runnable { + channelMessageLogger?.cleanupOldLogs( + configuration.features.channelChat.messageLogging.retentionDays, + ) + }, + initialDelay, + cleanupInterval, + ) + } } /** @@ -285,5 +329,6 @@ class ServiceInitializer( conversionCache?.saveToDisk() services.channelManager?.saveToDisk() services.chatModeManager?.shutdown() + channelMessageLogger?.flushSync() } } diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMessageLogger.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMessageLogger.kt new file mode 100644 index 0000000..d14b097 --- /dev/null +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMessageLogger.kt @@ -0,0 +1,191 @@ +package dev.m1sk9.lunaticChat.paper.chat.channel + +import dev.m1sk9.lunaticChat.engine.chat.channel.ChannelMessageLogEntry +import io.ktor.util.logging.Logger +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.bukkit.plugin.Plugin +import java.io.BufferedWriter +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import java.time.LocalDate +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.io.path.deleteIfExists +import kotlin.io.path.fileSize +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.name + +/** + * Asynchronous logger for channel messages. + * + * Writes messages in NDJSON format to daily rotated log files. + * Uses a concurrent queue and scheduled flushing to minimize performance impact. + * + * @property logsDirectory Directory where log files are stored + * @property plugin Bukkit plugin instance for scheduling tasks + * @property logger Logger for diagnostic messages + * @property maxFileSizeBytes Maximum size of a single log file + */ +class ChannelMessageLogger( + private val logsDirectory: Path, + private val plugin: Plugin, + private val logger: Logger, + private val maxFileSizeBytes: Long, +) { + private val pendingEntries = ConcurrentLinkedQueue() + private val json = Json { encodeDefaults = true } + private var flushTaskId: Int? = null + + companion object { + private const val LOG_FILE_PREFIX = "channel-messages-" + private const val LOG_FILE_EXTENSION = ".json" + private val DATE_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + private const val FLUSH_INTERVAL_TICKS = 20L // 1 second + } + + init { + // Ensure logs directory exists + try { + Files.createDirectories(logsDirectory) + logger.info("Channel message logger initialized at: $logsDirectory") + schedulePeriodicFlush() + } catch (e: Exception) { + logger.error("Failed to initialize channel message logger", e) + } + } + + /** + * Queues a message for asynchronous logging. + * + * @param entry The log entry to write + */ + fun logMessage(entry: ChannelMessageLogEntry) { + pendingEntries.offer(entry) + } + + /** + * Schedules periodic flushing of pending log entries. + */ + private fun schedulePeriodicFlush() { + flushTaskId = + plugin.server.scheduler + .runTaskTimerAsynchronously( + plugin, + Runnable { flushPendingEntries() }, + FLUSH_INTERVAL_TICKS, + FLUSH_INTERVAL_TICKS, + ).taskId + } + + /** + * Flushes all pending entries to the current day's log file. + */ + private fun flushPendingEntries() { + if (pendingEntries.isEmpty()) { + return + } + + val entries = mutableListOf() + while (true) { + val entry = pendingEntries.poll() ?: break + entries.add(entry) + } + + if (entries.isEmpty()) { + return + } + + try { + val logFile = getCurrentLogFile() + + // Check file size before writing + if (Files.exists(logFile) && logFile.fileSize() >= maxFileSizeBytes) { + logger.warn("Log file ${logFile.name} exceeded maximum size, skipping flush") + return + } + + BufferedWriter( + Files.newBufferedWriter( + logFile, + StandardOpenOption.CREATE, + StandardOpenOption.APPEND, + ), + ).use { writer -> + for (entry in entries) { + val jsonLine = json.encodeToString(entry) + writer.write(jsonLine) + writer.newLine() + } + } + } catch (e: Exception) { + logger.error("Failed to flush log entries", e) + // Re-queue entries for retry + entries.forEach { pendingEntries.offer(it) } + } + } + + /** + * Synchronously flushes all pending entries. + * Should be called during plugin shutdown. + */ + fun flushSync() { + // Cancel scheduled task + flushTaskId?.let { plugin.server.scheduler.cancelTask(it) } + + // Flush remaining entries + flushPendingEntries() + logger.info("Channel message logger flushed all pending entries") + } + + /** + * Deletes log files older than the specified retention period. + * + * @param retentionDays Number of days to retain log files + */ + fun cleanupOldLogs(retentionDays: Int) { + if (retentionDays <= 0) { + return + } + + try { + val cutoffDate = LocalDate.now(ZoneOffset.UTC).minusDays(retentionDays.toLong()) + val logFiles = logsDirectory.listDirectoryEntries("$LOG_FILE_PREFIX*$LOG_FILE_EXTENSION") + + var deletedCount = 0 + for (logFile in logFiles) { + val fileName = logFile.name + val dateStr = fileName.removePrefix(LOG_FILE_PREFIX).removeSuffix(LOG_FILE_EXTENSION) + + try { + val fileDate = LocalDate.parse(dateStr, DATE_FORMATTER) + if (fileDate.isBefore(cutoffDate)) { + logFile.deleteIfExists() + deletedCount++ + logger.info("Deleted old log file: $fileName") + } + } catch (e: Exception) { + logger.warn("Failed to parse date from log file: $fileName", e) + } + } + + if (deletedCount > 0) { + logger.info("Cleaned up $deletedCount old log file(s)") + } + } catch (e: Exception) { + logger.error("Failed to cleanup old logs", e) + } + } + + /** + * Gets the log file path for the current UTC date. + */ + private fun getCurrentLogFile(): Path { + val currentDate = LocalDate.now(ZoneOffset.UTC) + val dateStr = currentDate.format(DATE_FORMATTER) + val fileName = "$LOG_FILE_PREFIX$dateStr$LOG_FILE_EXTENSION" + return logsDirectory.resolve(fileName) + } +} diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/handler/ChannelMessageHandler.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/handler/ChannelMessageHandler.kt index 12426cf..810d1ca 100644 --- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/handler/ChannelMessageHandler.kt +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/handler/ChannelMessageHandler.kt @@ -1,6 +1,8 @@ package dev.m1sk9.lunaticChat.paper.chat.handler +import dev.m1sk9.lunaticChat.engine.chat.channel.ChannelMessageLogEntry import dev.m1sk9.lunaticChat.paper.chat.channel.ChannelManager +import dev.m1sk9.lunaticChat.paper.chat.channel.ChannelMessageLogger import dev.m1sk9.lunaticChat.paper.common.SpyPermissionManager import dev.m1sk9.lunaticChat.paper.common.playChannelReceiveNotification import dev.m1sk9.lunaticChat.paper.common.playMessageSendNotification @@ -20,6 +22,7 @@ class ChannelMessageHandler( private val channelManager: ChannelManager, private val romanjiConverter: RomanjiConverter?, private val languageManager: LanguageManager, + private val messageLogger: ChannelMessageLogger?, private val logger: Logger, ) { fun sendChannelMessage( @@ -92,6 +95,19 @@ class ChannelMessageHandler( } logger.info("Channel Message from ${player.name} in ${context.channel.name}: $message") + + // Log message to file if logging is enabled + messageLogger?.let { + val logEntry = + ChannelMessageLogEntry.create( + playerId = player.uniqueId, + playerName = player.name, + channelId = context.channelId, + message = message, + ) + it.logMessage(logEntry) + } + return true } diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/key/ChannelChatFeatureConfig.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/key/ChannelChatFeatureConfig.kt index 5b93ac5..9a64428 100644 --- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/key/ChannelChatFeatureConfig.kt +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/key/ChannelChatFeatureConfig.kt @@ -1,8 +1,12 @@ package dev.m1sk9.lunaticChat.paper.config.key +import kotlinx.serialization.Serializable + +@Serializable data class ChannelChatFeatureConfig( val enabled: Boolean, val maxChannelsPerServer: Int = 0, val maxMembersPerChannel: Int = 0, val maxMembershipPerPlayer: Int = 0, + val messageLogging: ChannelMessageLoggingConfig = ChannelMessageLoggingConfig(), ) diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/key/ChannelMessageLoggingConfig.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/key/ChannelMessageLoggingConfig.kt new file mode 100644 index 0000000..f036620 --- /dev/null +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/config/key/ChannelMessageLoggingConfig.kt @@ -0,0 +1,20 @@ +package dev.m1sk9.lunaticChat.paper.config.key + +import kotlinx.serialization.Serializable + +/** + * Configuration for channel message logging feature. + * + * Enables file-based logging of all channel messages in NDJSON format, + * compatible with log aggregation systems like Grafana Loki. + * + * @property enabled Whether message logging is enabled + * @property retentionDays Number of days to retain log files (0 = keep forever) + * @property maxFileSizeMB Maximum size of a single log file in megabytes + */ +@Serializable +data class ChannelMessageLoggingConfig( + val enabled: Boolean = true, + val retentionDays: Int = 30, + val maxFileSizeMB: Int = 100, +) diff --git a/platform-paper/src/main/resources/config.yml b/platform-paper/src/main/resources/config.yml index 8efa1fa..0889070 100644 --- a/platform-paper/src/main/resources/config.yml +++ b/platform-paper/src/main/resources/config.yml @@ -57,6 +57,14 @@ features: maxMembersPerChannel: 0 # Maximum number of channels a single player can join. Set to 0 for unlimited. maxMembershipPerPlayer: 0 + # Channel message logging configuration + messageLogging: + # If enabled, all channel messages will be logged to NDJSON files for analysis and archival. + enabled: true + # Number of days to retain log files. Set to 0 to keep logs indefinitely. + retentionDays: 30 + # Maximum size of a single log file in megabytes. Files exceeding this size will stop accepting new entries. + maxFileSizeMB: 100 # ---------------------------------------------- # --------- Message Format Settings -------- -- cgit v1.2.1 From 3f7021a3bd407f4133e76dab22cc68e31ffd018c Mon Sep 17 00:00:00 2001 From: Sho Sakuma Date: Sat, 31 Jan 2026 17:51:47 +0900 Subject: fix: Prevent other plugins from intercepting channel chat --- .../dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt index ec8804d..861707a 100644 --- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.runBlocking import net.kyori.adventure.text.Component import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority import org.bukkit.event.Listener class PlayerChatListener( @@ -25,7 +26,7 @@ class PlayerChatListener( ) : Listener { private val plainTextSerializer = PlainTextComponentSerializer.plainText() - @EventHandler(ignoreCancelled = true) + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) fun onChat(event: AsyncChatEvent) { val player = event.player val settings = settingsManager.getSettings(player.uniqueId) @@ -81,8 +82,13 @@ class PlayerChatListener( val hasActiveChannel = channelManager.getPlayerChannel(player.uniqueId) != null if (hasActiveChannel) { - // Send to channel as normal + // Cancel event and clear all data to prevent other plugins from capturing it + // Even MONITOR priority listeners with ignoreCancelled=false won't get useful data + // Message will be delivered by ChannelMessageHandler instead + // Logging is handled by our own ChannelMessageLogger event.isCancelled = true + event.viewers().clear() + event.message(Component.empty()) channelMessageHandler.sendChannelMessage(player, messageWithoutPrefix) } else { // Auto-fallback to global chat -- cgit v1.2.1 From 2b8f53067b7c497bb8c4a90d9dcc41e2d5ca78e6 Mon Sep 17 00:00:00 2001 From: Sho Sakuma Date: Sat, 31 Jan 2026 18:25:59 +0900 Subject: docs: Add channel chat log --- CHANGELOG.md | 13 +++ README.md | 3 +- build.gradle.kts | 2 +- docs/.vitepress/config/en.ts | 23 ++-- docs/.vitepress/config/ja.ts | 69 ++++++------ docs/biome.jsonc | 2 +- .../en/guide/admin/channel-chat/introduction.md | 74 +++++++++++++ docs/src/en/guide/admin/channel-chat/logs.md | 119 +++++++++++++++++++++ docs/src/en/guide/admin/configuration.md | 70 +++++++++++- docs/src/en/guide/admin/data-and-logs.md | 119 --------------------- .../en/guide/admin/introduction-channel-chat.md | 56 ---------- docs/src/en/guide/admin/management-data.md | 107 ++++++++++++++++++ docs/src/en/index.md | 3 - docs/src/guide/admin/channel-chat/introduction.md | 74 +++++++++++++ docs/src/guide/admin/channel-chat/logs.md | 119 +++++++++++++++++++++ docs/src/guide/admin/configuration.md | 33 ++++++ docs/src/guide/admin/data-and-logs.md | 119 --------------------- docs/src/guide/admin/introduction-channel-chat.md | 56 ---------- docs/src/guide/admin/management-data.md | 107 ++++++++++++++++++ docs/src/index.md | 3 - .../m1sk9/lunaticChat/paper/ServiceInitializer.kt | 23 +--- .../paper/chat/channel/ChannelMessageLogger.kt | 100 +++++++++++++---- .../paper/listener/PlayerChatListener.kt | 2 +- 23 files changed, 854 insertions(+), 442 deletions(-) create mode 100644 docs/src/en/guide/admin/channel-chat/introduction.md create mode 100644 docs/src/en/guide/admin/channel-chat/logs.md delete mode 100644 docs/src/en/guide/admin/data-and-logs.md delete mode 100644 docs/src/en/guide/admin/introduction-channel-chat.md create mode 100644 docs/src/en/guide/admin/management-data.md create mode 100644 docs/src/guide/admin/channel-chat/introduction.md create mode 100644 docs/src/guide/admin/channel-chat/logs.md delete mode 100644 docs/src/guide/admin/data-and-logs.md delete mode 100644 docs/src/guide/admin/introduction-channel-chat.md create mode 100644 docs/src/guide/admin/management-data.md diff --git a/CHANGELOG.md b/CHANGELOG.md index df917cd..6563e83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ ## v0 +### v0.7.0 + +#### Breaking Changes + +- Other plugins such as CoreProtect can no longer intercept channel chat. + - This has broken the integration functionality with CoreProtect. + - Channel Chat logs are now uniformly accessible via a text-based viewing method. + +---- + +- Channel chat logging functionality has been implemented. + - Logs are now recorded daily in `plugins/LunaticChat/logs/channelchat/`. + ### v0.6.0 - Added experimental feature to Channel Chat support. diff --git a/README.md b/README.md index ea2ec8d..13cb669 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,9 @@ See the [Documentation](https://lc.m1sk9.dev/guide/getting-started). - 1on1 Direct Messaging System (`/tell`, `/msg`) - Quick Reply Functionality (`/reply`) - Romaji to Japanese Conversion -- CoreProtect-compatible chat logging +- Channel Chat System - Multi-platform support (Paper, Velocity) (coming soon) - Spigot? No problem, just use Paper! -- Channel Chat System (coming soon) ## License diff --git a/build.gradle.kts b/build.gradle.kts index c275e7d..039b977 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,7 +14,7 @@ plugins { allprojects { group = "dev.m1sk9" - version = "0.6.0" + version = "0.7.0" repositories { mavenCentral() diff --git a/docs/.vitepress/config/en.ts b/docs/.vitepress/config/en.ts index 5718a4e..24a3691 100644 --- a/docs/.vitepress/config/en.ts +++ b/docs/.vitepress/config/en.ts @@ -18,6 +18,19 @@ export const en: DefaultTheme.Config = { { text: 'For Server Administrators', items: [ + { + text: 'Channel Chat', + items: [ + { + text: 'Deployment Guide', + link: '/en/guide/admin/channel-chat/introduction', + }, + { + text: 'Logs', + link: '/en/guide/admin/channel-chat/logs', + }, + ], + }, { text: 'Cache System', link: '/en/guide/admin/cache', @@ -31,12 +44,8 @@ export const en: DefaultTheme.Config = { link: '/en/guide/admin/velocity', }, { - text: 'Channel Chat Deployment Guide', - link: '/en/guide/admin/introduction-channel-chat', - }, - { - text: 'Data and Logs', - link: '/en/guide/admin/data-and-logs', + text: 'Data Management', + link: '/en/guide/admin/management-data', }, ], }, @@ -59,7 +68,7 @@ export const en: DefaultTheme.Config = { text: 'Moderation', link: '/en/guide/player/channel-chat/moderation', }, - ] + ], }, { text: 'Direct Messages', diff --git a/docs/.vitepress/config/ja.ts b/docs/.vitepress/config/ja.ts index 9882687..4beec42 100644 --- a/docs/.vitepress/config/ja.ts +++ b/docs/.vitepress/config/ja.ts @@ -18,6 +18,19 @@ export const ja: DefaultTheme.Config = { { text: 'サーバー管理者向け', items: [ + { + text: 'チャンネルチャット', + items: [ + { + text: '展開ガイド', + link: '/guide/admin/channel-chat/introduction', + }, + { + text: 'ログ', + link: '/guide/admin/channel-chat/logs', + }, + ], + }, { text: 'キャッシュシステム', link: '/guide/admin/cache', @@ -30,14 +43,10 @@ export const ja: DefaultTheme.Config = { text: 'Velocity 連携', link: '/guide/admin/velocity', }, - { - text: "チャンネルチャット展開ガイド", - link: "/guide/admin/introduction-channel-chat", - }, - { - text: "データ・ログ", - link: "/guide/admin/data-and-logs", - } + { + text: 'データの管理', + link: '/guide/admin/management-data', + }, ], }, { @@ -45,21 +54,21 @@ export const ja: DefaultTheme.Config = { items: [ { text: 'チャンネルチャット', - link: "/guide/player/channel-chat/about", + link: '/guide/player/channel-chat/about', items: [ - { - text: 'チャットモード', - link: '/guide/player/channel-chat/chatmode', - }, - { - text: 'プライベートチャンネル', - link: '/guide/player/channel-chat/private-channel', - }, - { - text: 'モデレーション', - link: '/guide/player/channel-chat/moderation', - }, - ] + { + text: 'チャットモード', + link: '/guide/player/channel-chat/chatmode', + }, + { + text: 'プライベートチャンネル', + link: '/guide/player/channel-chat/private-channel', + }, + { + text: 'モデレーション', + link: '/guide/player/channel-chat/moderation', + }, + ], }, { text: 'ダイレクトメッセージ', @@ -120,14 +129,14 @@ export const ja: DefaultTheme.Config = { text: '/lc status', link: '/reference/commands/lc/status', }, - { - text: '/lc channel', - link: '/reference/commands/lc/channel', - }, - { - text: '/lc chatmode', - link: '/reference/commands/lc/chatmode', - }, + { + text: '/lc channel', + link: '/reference/commands/lc/channel', + }, + { + text: '/lc chatmode', + link: '/reference/commands/lc/chatmode', + }, ], }, ], diff --git a/docs/biome.jsonc b/docs/biome.jsonc index 5d91e18..7b0325f 100644 --- a/docs/biome.jsonc +++ b/docs/biome.jsonc @@ -2,7 +2,7 @@ "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json", "files": { "ignoreUnknown": true, - "includes": [".vitepress/config.mts", "biome.jsonc"] + "includes": [".vitepress/config.mts", "biome.jsonc", ".vitepress/config/**"] }, "formatter": { "enabled": true, diff --git a/docs/src/en/guide/admin/channel-chat/introduction.md b/docs/src/en/guide/admin/channel-chat/introduction.md new file mode 100644 index 0000000..e755139 --- /dev/null +++ b/docs/src/en/guide/admin/channel-chat/introduction.md @@ -0,0 +1,74 @@ +# Channel Chat: Deployment Guide + +This guide explains how to deploy channel chat. + +## What is Channel Chat + +Channel chat is a feature that allows players to create channels and share chat among specific players. + +For detailed features, please refer to the [Player Guide](../../player/channel-chat/about.md). + +## Preparing to Deploy Channel Chat + +To deploy channel chat, you need to enable the channel chat feature in the LunaticChat configuration file `config.yml`. + +1. Stop the server. +2. Open `plugins/LunaticChat/config.yml`. +3. Set `features.channelChat.enabled` to `true`. +4. Restart the server. + +This will enable the channel chat feature and allow players to use channel chat. + +## Channel Chat Configuration + +The configuration items related to channel chat are as follows: + +- `features.channelChat.maxChannelsPerPlayer`: Specifies the maximum number of channels a single player can create. +- `features.channelChat.maxMembersPerChannel`: Specifies the maximum number of members that can join a single channel. +- `features.channelChat.maxMembershipPerPlayer`: Specifies the maximum number of channels a single player can join. + +The default is set to `0`, which means there is no limit. + +::: tip Recommended Settings + +If you want to actively use the channel chat feature, we recommend setting these values higher. + +However, as this may impact server performance, please set them appropriately according to your server's resource situation. + +Recommended settings are as follows: + +- `features.channelChat.maxChannelsPerPlayer`: `3` to `5` +- `features.channelChat.maxMembersPerChannel`: `20` to `50` +- `features.channelChat.maxMembershipPerPlayer`: `5` to `10` + +::: + +## Channel Chat Logging + +::: warning Compatibility with Plugins like CoreProtect + +As of v0.7.0, LunaticChat's channel chat feature is not compatible with logging plugins like CoreProtect. + +::: + +Channel chat logging is enabled by default. + +For more details, see [Channel Chat: Logs](logs.md). + +## Channel Management + +Basically, [players manage channels themselves](../../player/channel-chat/moderation.md). + +However, as a server administrator, please note the following: + +- Server administrators have owner permissions for all channels. If operations are required, please respond appropriately. +- To maintain server performance, set limits on the number of channels and members as needed. +- If inappropriate channels or member behavior occurs, take appropriate action. + +If you want to avoid troubles related to channel management, consider restricting moderate commands such as `/lc channel ban`. + +## Plugins that Intercept Channel Chat + +Logging plugins like CoreProtect do not intercept LunaticChat's channel chat messages by default. + +However, plugins that use Paper API's `originalMessage()` to retrieve messages may intercept LunaticChat's channel chat messages. diff --git a/docs/src/en/guide/admin/channel-chat/logs.md b/docs/src/en/guide/admin/channel-chat/logs.md new file mode 100644 index 0000000..b65750b --- /dev/null +++ b/docs/src/en/guide/admin/channel-chat/logs.md @@ -0,0 +1,119 @@ +# Channel Chat: Logs + +Channel chat logs are a record of all messages and activities that occur within the chat. + +::: warning Compatibility with Plugins like CoreProtect + +As of v0.7.0, LunaticChat's channel chat feature is not compatible with logging plugins like CoreProtect. + +::: + +## Checking Channel Chat Logs + +Channel chat logs are stored in the `plugins/LunaticChat/logs/channelchat/` directory. + +Channel chat log files are saved in the following format: + +``` +{"timestamp":"2026-01-31T08:54:18.504343071Z","playerId":"ceaea267-39dd-3bac-931c-761ada671ebe","playerName":"m1sk9","channelId":"test","message":"Hello"} +``` + +## About File Size + +Each line is a complete JSON object, separated by line breaks, and the file as a whole is not a JSON array. + +```text +plugins/LunaticChat/logs/ +├── channel-messages-2026-01-17.json (5.2 MB) +├── channel-messages-2026-01-18.json (4.8 MB) +├── channel-messages-2026-01-19.json (6.1 MB) +├── channel-messages-2026-01-20.json (5.5 MB) +├── channel-messages-2026-01-21.json (7.2 MB) <- Weekend, active +├── channel-messages-2026-01-22.json (6.9 MB) +├── channel-messages-2026-01-23.json (4.3 MB) +├── channel-messages-2026-01-24.json (5.0 MB) +├── channel-messages-2026-01-25.json (5.4 MB) +├── channel-messages-2026-01-26.json (4.9 MB) +├── channel-messages-2026-01-27.json (6.2 MB) +├── channel-messages-2026-01-28.json (7.5 MB) +├── channel-messages-2026-01-29.json (5.8 MB) +├── channel-messages-2026-01-30.json (6.0 MB) +└── channel-messages-2026-01-31.json (2.1 MB) <- Today (in progress) +``` + +Total: approximately 83 MB + +With a 30-day retention setting, the January 17 file will be automatically deleted tomorrow. + +### Size per Message + +Channel chat log entries are approximately 200 bytes per line. + +Assuming 1000 messages per hour: + +```text +1000 msg/h × 24h × 220 bytes = 5,280,000 bytes ≈ 5.3 MB/day +``` + +With the default 30-day retention setting, this amounts to approximately **159 MB**. + +```text +5.3 MB × 30 days = 159 MB +``` + +## Visualization with Grafana Loki + +Due to the JSON format, using Promtail to ingest channel chat logs into Grafana Loki will parse them for easier reading. + +```text +2026-01-31 10:23:45.123 {job="lunatichat", player="Steve", channel="Global"} +Hello everyone! + +2026-01-31 10:24:12.456 {job="lunatichat", player="Alex", channel="Global"} +Hi Steve! + +2026-01-31 10:25:03.789 {job="lunatichat", player="Notch", channel="Development Team"} +Working on new features +``` + +::: tip Filtering Examples + +Examples of querying logs in Grafana Loki: + +```text +{job="lunatichat"} |= "new features" +{job="lunatichat", channel="Global"} +{job="lunatichat", player="Steve"} +``` + +::: + +## Command Line Checking Examples + +### View the latest 10 entries + +```bash +tail -n 10 plugins/LunaticChat/logs/channel-messages-2026-01-31.json | jq +``` + +### Extract messages from a specific player + +```bash +cat plugins/LunaticChat/logs/channel-messages-*.json | \ +jq 'select(.playerName=="Steve")' +``` + +### Count messages from a specific channel + +```bash +cat plugins/LunaticChat/logs/channel-messages-*.json | \ +jq 'select(.channelId=="global")' | wc -l +``` + +### Message count by date + +```bash +for file in plugins/LunaticChat/logs/channel-messages-*.json; do +echo "$file: $(wc -l < $file) messages" +done +``` diff --git a/docs/src/en/guide/admin/configuration.md b/docs/src/en/guide/admin/configuration.md index 6ce63b4..ef33ddc 100644 --- a/docs/src/en/guide/admin/configuration.md +++ b/docs/src/en/guide/admin/configuration.md @@ -54,6 +54,20 @@ features: channelChat: # If enabled, channel-based chat functionality will be activated. enabled: false + # Maximum number of channels that can be created per server. Set to 0 for unlimited. + maxChannelsPerServer: 0 + # Maximum number of members allowed in a single channel. Set to 0 for unlimited. + maxMembersPerChannel: 0 + # Maximum number of channels a single player can join. Set to 0 for unlimited. + maxMembershipPerPlayer: 0 + # Channel message logging configuration + messageLogging: + # If enabled, all channel messages will be logged to NDJSON files for analysis and archival. + enabled: true + # Number of days to retain log files. Set to 0 to keep logs indefinitely. + retentionDays: 30 + # Maximum size of a single log file in megabytes. Files exceeding this size will stop accepting new entries. + maxFileSizeMB: 100 # ---------------------------------------------- # --------- Message Format Settings -------- @@ -171,13 +185,67 @@ Specifies the timeout duration (in milliseconds) for API requests to the romaniz Specifies the number of retry attempts for failed API requests to the romanization conversion service. -### `features.channelChat.enabled` +### `features.channelChat` + +#### `enabled` - Type: `boolean` - Default: `false` Enables channel-based chat functionality. +#### `maxChannelsPerServer` + +- Type: `integer` +- Default: `0` + +Specifies the maximum number of channels that can be created per server. + +Set to `0` for unlimited. + +#### `maxMembersPerChannel` + +- Type: `integer` +- Default: `0` + +Specifies the maximum number of members that can join a single channel. + +Set to `0` for unlimited. + +#### `maxMembershipPerPlayer` + +- Type: `integer` +- Default: `0` + +Specifies the maximum number of channels a single player can join. + +Set to `0` for unlimited. + +#### `messageLogging.enabled` + +- Type: `boolean` +- Default: `true` + +Specifies whether to log channel chat messages in NDJSON format. + +#### `messageLogging.retentionDays` + +- Type: `integer` +- Default: `30` + +Specifies the number of days to retain channel chat log files. + +Set to `0` to never delete log files. + +#### `messageLogging.maxFileSizeMB` + +- Type: `integer` +- Default: `100` + +Specifies the maximum size (in megabytes) of channel chat log files. + +Log files exceeding `maxFileSizeMB` will stop accepting new entries. + ## Message Format Settings Available placeholders: diff --git a/docs/src/en/guide/admin/data-and-logs.md b/docs/src/en/guide/admin/data-and-logs.md deleted file mode 100644 index e869552..0000000 --- a/docs/src/en/guide/admin/data-and-logs.md +++ /dev/null @@ -1,119 +0,0 @@ -# Data and Logs - -::: danger Do Not Edit - -These data files are essential to the operation of LunaticChat. Direct editing may cause data corruption or unexpected behavior. Do not directly edit these files unless you are backing up data. - -::: - -## Data Storage Location - -LunaticChat saves channel data and configuration information to local disk. - -- `channels.json`: Stores channel information. -- `chatmodes.json`: Stores player chat mode settings. -- `conversion_cache.json`: Stores cache for channel conversion. -- `player-settings.yaml`: Stores player-specific settings. - -::: tip Regular Backups - -To ensure the safety of your LunaticChat data, we recommend creating regular backups. - -::: - -### `channels.json` - -The `channels.json` file stores information about channels managed by LunaticChat. This file contains information such as channel names, participant lists, and chat modes. - -```json -{ - "channels": { - "general-channel": { - "id": "general-channel", - "name": "General Channel", - "ownerId": "a01e3843-e521-3998-958a-f459800e4d11", - "createdAt": 1769507213150, - "bannedPlayers": [ - "ceaea267-39dd-3bac-931c-761ada671ebe" - ] - } - }, - "members": { - "general-channel": [ - { - "channelId": "test2", - "playerId": "a01e3843-e521-3998-958a-f459800e4d11", - "role": "OWNER", - "joinedAt": 1769507213150 - } - ] - }, - "activeChannels": { - "a01e3843-e521-3998-958a-f459800e4d11": "test2" - } -} -``` - -### `chatmodes.json` - -The `chatmodes.json` file stores player chat mode settings. This file contains chat mode information for each player. - -```json -{ - "modes": { - "aed5efd4-551b-3965-bc28-ae21aa072a66": "CHANNEL", - "ceaea267-39dd-3bac-931c-761ada671ebe": "CHANNEL", - "a01e3843-e521-3998-958a-f459800e4d11": "CHANNEL", - "681f539b-8bb8-3f85-85e5-a2945f6c6539": "GLOBAL" - } -} -``` - -### `conversion_cache.json` - -The `conversion_cache.json` file stores cache for channel conversion. This file contains channel conversion information for each player. - -For more information about the cache system, see [here](./cache.md). - - -```json -{"version":"1","entries":{"hi":"日"}} -``` - -### `player-settings.yaml` - -The `player-settings.yaml` file stores player-specific settings. This file contains individual player settings. - -```yaml -version: 1 -japaneseConversion: - "aed5efd4-551b-3965-bc28-ae21aa072a66": false - "ceaea267-39dd-3bac-931c-761ada671ebe": false -directMessageNotification: - "aed5efd4-551b-3965-bc28-ae21aa072a66": true - "ceaea267-39dd-3bac-931c-761ada671ebe": true -channelMessageNotification: - "ceaea267-39dd-3bac-931c-761ada671ebe": true -``` - -## Cache Version - -Files used for disk caching include a `version` field to accommodate changes in cache format as LunaticChat is upgraded. - -If the version does not match, LunaticChat recognizes the cache file as **old format cache**, ignores the contents, and recreates it in the new format. - -```json -{"version":"1","entries":{}} -``` - -## About CoreProtect - -Various chat logs in LunaticChat can also be recorded in CoreProtect without requiring an API. - -Logs for each feature can be checked with the following actions. When using the `/co lookup` command, specify the following actions: - -- Direct messages: `command` -- Global chat and channel chat: `chat` - - Japanese and romanization conversion is saved according to the player's settings. - - diff --git a/docs/src/en/guide/admin/introduction-channel-chat.md b/docs/src/en/guide/admin/introduction-channel-chat.md deleted file mode 100644 index 2a81d63..0000000 --- a/docs/src/en/guide/admin/introduction-channel-chat.md +++ /dev/null @@ -1,56 +0,0 @@ -# Channel Chat Deployment Guide - -This guide explains how to deploy channel chat. - -## What is Channel Chat - -Channel chat is a feature that allows players to create channels and share chat among specific players. - -For detailed features, please refer to the [Player Guide](../player/channel-chat/about.md). - -## Preparing to Deploy Channel Chat - -To deploy channel chat, you need to enable the channel chat feature in the LunaticChat configuration file `config.yml`. - -1. Stop the server. -2. Open `plugins/LunaticChat/config.yml`. -3. Set `features.channelChat.enabled` to `true`. -4. Restart the server. - -This will enable the channel chat feature and allow players to use channel chat. - -## Channel Chat Configuration - -The configuration items related to channel chat are as follows: - -- `features.channelChat.maxChannelsPerPlayer`: Specifies the maximum number of channels a single player can create. -- `features.channelChat.maxMembersPerChannel`: Specifies the maximum number of members that can join a single channel. -- `features.channelChat.maxMembershipPerPlayer`: Specifies the maximum number of channels a single player can join. - -The default is set to `0`, which means there is no limit. - -::: tip Recommended Settings - -If you want to actively use the channel chat feature, we recommend setting these values higher. - -However, as this may impact server performance, please set them appropriately according to your server's resource situation. - -Recommended settings are as follows: - -- `features.channelChat.maxChannelsPerPlayer`: `3` to `5` -- `features.channelChat.maxMembersPerChannel`: `20` to `50` -- `features.channelChat.maxMembershipPerPlayer`: `5` to `10` - -::: - -## Channel Management - -Basically, [players manage channels themselves](../player/channel-chat/moderation.md). - -However, as a server administrator, please note the following: - -- Server administrators have owner permissions for all channels. If operations are required, please respond appropriately. -- To maintain server performance, set limits on the number of channels and members as needed. -- If inappropriate channels or member behavior occurs, take appropriate action. - -If you want to avoid troubles related to channel management, consider restricting moderate commands such as `/lc channel ban`. diff --git a/docs/src/en/guide/admin/management-data.md b/docs/src/en/guide/admin/management-data.md new file mode 100644 index 0000000..ffed59d --- /dev/null +++ b/docs/src/en/guide/admin/management-data.md @@ -0,0 +1,107 @@ +# Data and Logs + +::: danger Do Not Edit + +These data files are essential to the operation of LunaticChat. Direct editing may cause data corruption or unexpected behavior. Do not directly edit these files unless you are backing up data. + +::: + +## Data Storage Location + +LunaticChat saves channel data and configuration information to local disk. + +- `channels.json`: Stores channel information. +- `chatmodes.json`: Stores player chat mode settings. +- `conversion_cache.json`: Stores cache for channel conversion. +- `player-settings.yaml`: Stores player-specific settings. + +::: tip Regular Backups + +To ensure the safety of your LunaticChat data, we recommend creating regular backups. + +::: + +### `channels.json` + +The `channels.json` file stores information about channels managed by LunaticChat. This file contains information such as channel names, participant lists, and chat modes. + +```json +{ + "channels": { + "general-channel": { + "id": "general-channel", + "name": "General Channel", + "ownerId": "a01e3843-e521-3998-958a-f459800e4d11", + "createdAt": 1769507213150, + "bannedPlayers": [ + "ceaea267-39dd-3bac-931c-761ada671ebe" + ] + } + }, + "members": { + "general-channel": [ + { + "channelId": "test2", + "playerId": "a01e3843-e521-3998-958a-f459800e4d11", + "role": "OWNER", + "joinedAt": 1769507213150 + } + ] + }, + "activeChannels": { + "a01e3843-e521-3998-958a-f459800e4d11": "test2" + } +} +``` + +### `chatmodes.json` + +The `chatmodes.json` file stores player chat mode settings. This file contains chat mode information for each player. + +```json +{ + "modes": { + "aed5efd4-551b-3965-bc28-ae21aa072a66": "CHANNEL", + "ceaea267-39dd-3bac-931c-761ada671ebe": "CHANNEL", + "a01e3843-e521-3998-958a-f459800e4d11": "CHANNEL", + "681f539b-8bb8-3f85-85e5-a2945f6c6539": "GLOBAL" + } +} +``` + +### `conversion_cache.json` + +The `conversion_cache.json` file stores cache for channel conversion. This file contains channel conversion information for each player. + +For more information about the cache system, see [here](./cache.md). + + +```json +{"version":"1","entries":{"hi":"日"}} +``` + +### `player-settings.yaml` + +The `player-settings.yaml` file stores player-specific settings. This file contains individual player settings. + +```yaml +version: 1 +japaneseConversion: + "aed5efd4-551b-3965-bc28-ae21aa072a66": false + "ceaea267-39dd-3bac-931c-761ada671ebe": false +directMessageNotification: + "aed5efd4-551b-3965-bc28-ae21aa072a66": true + "ceaea267-39dd-3bac-931c-761ada671ebe": true +channelMessageNotification: + "ceaea267-39dd-3bac-931c-761ada671ebe": true +``` + +## Cache Version + +Files used for disk caching include a `version` field to accommodate changes in cache format as LunaticChat is upgraded. + +If the version does not match, LunaticChat recognizes the cache file as **old format cache**, ignores the contents, and recreates it in the new format. + +```json +{"version":"1","entries":{}} +``` diff --git a/docs/src/en/index.md b/docs/src/en/index.md index c88e080..0fe8a53 100644 --- a/docs/src/en/index.md +++ b/docs/src/en/index.md @@ -32,9 +32,6 @@ features: - title: Japanese Romanization Conversion details: Automatically converts messages input in romaji to Japanese icon: 🌍 - - title: CoreProtect Support - details: LunaticChat's chat logs are compatible with CoreProtect - icon: 🗒️ - title: Channel Chat Feature (Planned) details: Create and manage chat channels, send private messages between specific players icon: ☎️ diff --git a/docs/src/guide/admin/channel-chat/introduction.md b/docs/src/guide/admin/channel-chat/introduction.md new file mode 100644 index 0000000..cf9fe52 --- /dev/null +++ b/docs/src/guide/admin/channel-chat/introduction.md @@ -0,0 +1,74 @@ +# チャンネルチャット: 展開ガイド + +このガイドでは、チャンネルチャットの展開方法について説明します. + +## チャンネルチャットとは + +プレイヤー間でチャンネルを作成し,特定のプレイヤー間で,チャットを共有できる機能です. + +詳しい機能については [プレイヤー向けガイド版](../../player/channel-chat/about.md) を参照してください. + +## チャンネルチャットの展開準備 + +チャンネルチャットを展開するには,LunaticChat の設定ファイル `config.yml` でチャンネルチャット機能を有効化する必要があります. + +1. サーバーを停止します. +2. `plugins/LunaticChat/config.yml` を開きます. +3. `features.channelChat.enabled` を `true` に設定します. +4. サーバーを再起動します. + +以上でチャンネルチャット機能が有効化され,プレイヤーがチャンネルチャットを使用できるようになります. + +## チャンネルチャットの設定 + +チャンネルチャットに関する設定項目は以下の通りです. + +- `features.channelChat.maxChannelsPerPlayer`: 1人のプレイヤーが作成できるチャンネルの最大数を指定します. +- `features.channelChat.maxMembersPerChannel`: 1つのチャンネルに参加できるメンバーの最大数を指定します. +- `features.channelChat.maxMembershipPerPlayer`: 1人のプレイヤーが参加できるチャンネルの最大数を指定します. + +デフォルトは `0` に設定されており,制限はありません. + +::: tip 設定のおすすめは? + +チャンネルチャット機能を活発に使用したい場合は,これらの値を高めに設定することをお勧めします. + +ただし,サーバーのパフォーマンスに影響を与える可能性があるため,サーバーのリソース状況に応じて適切に設定してください. + +おすすめの設定は次のとおりです. + +- `features.channelChat.maxChannelsPerPlayer`: `3` 〜 `5` +- `features.channelChat.maxMembersPerChannel`: `20` 〜 `50` +- `features.channelChat.maxMembershipPerPlayer`: `5` 〜 `10` + +::: + +## チャンネルチャットのログ + +::: warning CoreProtect などのプラグインとの互換性 + +LunaticChat では,チャンネルチャット機能に限り v0.7.0 以降 CoreProtect などのログ記録プラグインと互換性はありません. + +::: + +チャンネルチャットのログ機能はデフォルトで有効になっています. + +詳しくは [チャンネルチャット: ログ](logs.md) を参照してください. + +## チャンネルの管理 + +基本的に,[チャンネルの管理はプレイヤー自身が行います](../../player/channel-chat/moderation.md). + +ただし,サーバー管理者として,以下の点に注意してください: + +- サーバー管理者はすべてのチャンネルに対してオーナー権限を持ちます.操作が必要な場合は,適切に対応してください. +- サーバーのパフォーマンスを維持するために,必要に応じてチャンネル数やメンバー数の制限を設定してください. +- 不適切なチャンネルやメンバー行動が発生した場合は,適切な措置を講じてください. + +また,それらのチャンネルの管理によるトラブルを回避したい場合は `/lc channel ban` などのモデレートコマンドの制限を検討してください. + +## チャンネルチャットを捕捉してしまうプラグイン + +基本的に CoreProtect などのログ記録プラグインは,LunaticChat のチャンネルチャットメッセージを捕捉しません. + +ただし,Paper API の `originalMessage()` を使用してメッセージを取得しているプラグインは,LunaticChat のチャンネルチャットメッセージを捕捉してしまう可能性があります. diff --git a/docs/src/guide/admin/channel-chat/logs.md b/docs/src/guide/admin/channel-chat/logs.md new file mode 100644 index 0000000..9b88bdd --- /dev/null +++ b/docs/src/guide/admin/channel-chat/logs.md @@ -0,0 +1,119 @@ +# チャンネルチャット: ログ + +チャンネルチャットのログは,チャット内で行われたすべてのメッセージとアクティビティの記録です. + +::: warning CoreProtect などのプラグインとの互換性 + +LunaticChat では,チャンネルチャット機能に限り v0.7.0 以降 CoreProtect などのログ記録プラグインと互換性はありません. + +::: + +## チャンネルチャットのログを確認する + +チャンネルチャットのログは `plugins/LunaticChat/logs/channelchat/` ディレクトリに保存されます. + +チャンネルチャットのログファイルは,以下のフォーマットで保存されます: + +``` +{"timestamp":"2026-01-31T08:54:18.504343071Z","playerId":"ceaea267-39dd-3bac-931c-761ada671ebe","playerName":"m1sk9","channelId":"test","message":"こんにちは"} +``` + +## ファイルサイズについて + +各行が1つの完全な JSON オブジェクトであり,各行で区切られているだけで,ファイル全体としては JSON 配列ではありません. + +```text +plugins/LunaticChat/logs/ +├── channel-messages-2026-01-17.json (5.2 MB) +├── channel-messages-2026-01-18.json (4.8 MB) +├── channel-messages-2026-01-19.json (6.1 MB) +├── channel-messages-2026-01-20.json (5.5 MB) +├── channel-messages-2026-01-21.json (7.2 MB) <- 週末、アクティブ +├── channel-messages-2026-01-22.json (6.9 MB) +├── channel-messages-2026-01-23.json (4.3 MB) +├── channel-messages-2026-01-24.json (5.0 MB) +├── channel-messages-2026-01-25.json (5.4 MB) +├── channel-messages-2026-01-26.json (4.9 MB) +├── channel-messages-2026-01-27.json (6.2 MB) +├── channel-messages-2026-01-28.json (7.5 MB) +├── channel-messages-2026-01-29.json (5.8 MB) +├── channel-messages-2026-01-30.json (6.0 MB) +└── channel-messages-2026-01-31.json (2.1 MB) <- 今日(進行中) +``` + +合計: 約 83 MB + +30日保持設定 の場合,1月17日のファイルは明日自動削除されます. + +### 1メッセージあたりのサイズ + +チャンネルチャットのログエントリは1行につき,約200バイトです. + +1時間に1000メッセージの計算として + +```text +1000 msg/h × 24h × 220 bytes = 5,280,000 bytes ≈ 5.3 MB/日 +``` + +デフォルト設定の30日間保持の場合は **159 MB** 程度になります. + +```text +5.3 MB × 30日 = 159 MB +``` + +## Grafana Loki での可視化 + +JSON 形式のため,Promtail を使用し,Grafana Loki にチャンネルチャットのログを取り込むとパースされ読みやすくなります. + +```text +2026-01-31 10:23:45.123 {job="lunatichat", player="Steve", channel="Global"} +Hello everyone! + +2026-01-31 10:24:12.456 {job="lunatichat", player="Alex", channel="Global"} +Hi Steve! + +2026-01-31 10:25:03.789 {job="lunatichat", player="Notch", channel="Development Team"} +Working on new features +``` + +::: tip フィルタリング例 + +Grafana Loki でログをクエリ化する例: + +```text +{job="lunatichat"} |= "new features" +{job="lunatichat", channel="Global"} +{job="lunatichat", player="Steve"} +``` + +::: + +## コマンドラインでの確認例 + +### 最新10件を見る + +```bash +tail -n 10 plugins/LunaticChat/logs/channel-messages-2026-01-31.json | jq +``` + +### 特定プレイヤーのメッセージを抽出 + +```bash +cat plugins/LunaticChat/logs/channel-messages-*.json | \ +jq 'select(.playerName=="Steve")' +``` + +### 特定チャンネルのメッセージ数をカウント + +```bash +cat plugins/LunaticChat/logs/channel-messages-*.json | \ +jq 'select(.channelId=="global")' | wc -l +``` + +### 日付別メッセージ数 + +```bash +for file in plugins/LunaticChat/logs/channel-messages-*.json; do +echo "$file: $(wc -l < $file) messages" +done +``` diff --git a/docs/src/guide/admin/configuration.md b/docs/src/guide/admin/configuration.md index dfa41f9..5eb5fc6 100644 --- a/docs/src/guide/admin/configuration.md +++ b/docs/src/guide/admin/configuration.md @@ -60,6 +60,14 @@ features: maxMembersPerChannel: 0 # Maximum number of channels a single player can join. Set to 0 for unlimited. maxMembershipPerPlayer: 0 + # Channel message logging configuration + messageLogging: + # If enabled, all channel messages will be logged to NDJSON files for analysis and archival. + enabled: true + # Number of days to retain log files. Set to 0 to keep logs indefinitely. + retentionDays: 30 + # Maximum size of a single log file in megabytes. Files exceeding this size will stop accepting new entries. + maxFileSizeMB: 100 # ---------------------------------------------- # --------- Message Format Settings -------- @@ -213,6 +221,31 @@ LunaticChat の [`/reply`](../../reference/commands/reply.md) コマンドによ `0` に設定すると無制限になります. +#### `messageLogging.enabled` + +- Type: `boolean` +- Default: `true` + +チャンネルチャットのメッセージを NDJSON 形式でログに記録するかどうかを指定します. + +#### `messageLogging.retentionDays` + +- Type: `integer` +- Default: `30` + +チャンネルチャットのログファイルを保存する日数を指定します. + +`0` に設定すると,ログファイルは削除されません. + +#### `messageLogging.maxFileSizeMB` + +- Type: `integer` +- Default: `100` + +チャンネルチャットのログファイルの最大サイズ(メガバイト)を指定します. + +`maxFileSizeMB` を超えたログファイルは新しいエントリを受け付けなくなります. + ## Message Format Settings 使用できるプレースホルダー: diff --git a/docs/src/guide/admin/data-and-logs.md b/docs/src/guide/admin/data-and-logs.md deleted file mode 100644 index 6f48857..0000000 --- a/docs/src/guide/admin/data-and-logs.md +++ /dev/null @@ -1,119 +0,0 @@ -# データ・ログ - -::: danger 編集厳禁 - -これらのデータファイルは LunaticChat の動作に不可欠です.直接編集すると,データの破損や予期せぬ動作を引き起こす可能性があります.データのバックアップを取る場合を除き,これらのファイルを直接編集しないでください. - -::: - -## データの保存場所 - -LunaticChat は、チャンネルデータや設定情報をローカルディスクに保存します. - -- `channels.json`: チャンネル情報を保存するファイルです. -- `chatmodes.json`: プレイヤーのチャットモード設定を保存するファイルです. -- `conversion_cache.json`: チャンネル変換のキャッシュを保存するファイルです. -- `player-settings.yaml`: プレイヤーごとの設定情報を保存するファイルです. - -::: tip 定期的なバックアップ - -LunaticChat のデータの安全性を確保するために,定期的にバックアップを作成することをお勧めします. - -::: - -### `channels.json` - -`channels.json` ファイルは、LunaticChat が管理するチャンネルの情報を保存します.このファイルには,チャンネル名、参加者リスト、チャットモードなどの情報が含まれます. - -```json -{ - "channels": { - "general-channel": { - "id": "general-channel", - "name": "一般チャンネル", - "ownerId": "a01e3843-e521-3998-958a-f459800e4d11", - "createdAt": 1769507213150, - "bannedPlayers": [ - "ceaea267-39dd-3bac-931c-761ada671ebe" - ] - } - }, - "members": { - "general-channel": [ - { - "channelId": "test2", - "playerId": "a01e3843-e521-3998-958a-f459800e4d11", - "role": "OWNER", - "joinedAt": 1769507213150 - } - ] - }, - "activeChannels": { - "a01e3843-e521-3998-958a-f459800e4d11": "test2" - } -} -``` - -### `chatmodes.json` - -`chatmodes.json` ファイルは、プレイヤーのチャットモード設定を保存します.このファイルには,プレイヤーごとのチャットモード情報が含まれます. - -```json -{ - "modes": { - "aed5efd4-551b-3965-bc28-ae21aa072a66": "CHANNEL", - "ceaea267-39dd-3bac-931c-761ada671ebe": "CHANNEL", - "a01e3843-e521-3998-958a-f459800e4d11": "CHANNEL", - "681f539b-8bb8-3f85-85e5-a2945f6c6539": "GLOBAL" - } -} -``` - -### `conversion_cache.json` - -`conversion_cache.json` ファイルは、チャンネル変換のキャッシュを保存します.このファイルには,プレイヤーごとのチャンネル変換情報が含まれます. - -キャッシュシステムに関する詳細は [こちら](./cache.md) をご覧ください. - - -```json -{"version":"1","entries":{"hi":"日"}} -``` - -### `player-settings.yaml` - -`player-settings.yaml` ファイルは、プレイヤーごとの設定情報を保存します.このファイルには,プレイヤーの個別設定が含まれます. - -```yaml -version: 1 -japaneseConversion: - "aed5efd4-551b-3965-bc28-ae21aa072a66": false - "ceaea267-39dd-3bac-931c-761ada671ebe": false -directMessageNotification: - "aed5efd4-551b-3965-bc28-ae21aa072a66": true - "ceaea267-39dd-3bac-931c-761ada671ebe": true -channelMessageNotification: - "ceaea267-39dd-3bac-931c-761ada671ebe": true -``` - -## キャッシュバージョン - -ディスクキャッシュに使用されるファイルには `version` フィールドが含まれており,LunaticChat のバージョンアップに伴うキャッシュフォーマットの変更に対応しています. - -バージョンが不一致の場合,LunaticChat はキャッシュファイルを **古い形式のキャッシュ** として認識し,内容を無視して新しい形式で再作成します. - -```json -{"version":"1","entries":{}} -``` - -## CoreProtect について - -LunaticChat の各種チャットログは API 不要で CoreProtect でも記録することができます. - -各機能のログは次のアクションで確認できます. `/co lookup` コマンド使用時に以下のアクションを指定してください: - -- ダイレクトメッセージ: `command` -- 全体チャット・チャンネルチャット: `chat` - - かな・ローマ字変換はそのプレイヤーの設定状況により保存されます. - - diff --git a/docs/src/guide/admin/introduction-channel-chat.md b/docs/src/guide/admin/introduction-channel-chat.md deleted file mode 100644 index 6993435..0000000 --- a/docs/src/guide/admin/introduction-channel-chat.md +++ /dev/null @@ -1,56 +0,0 @@ -# チャンネルチャット展開ガイド - -このガイドでは、チャンネルチャットの展開方法について説明します. - -## チャンネルチャットとは - -プレイヤー間でチャンネルを作成し,特定のプレイヤー間で,チャットを共有できる機能です. - -詳しい機能については [プレイヤー向けガイド版](../player/channel-chat/about.md) を参照してください. - -## チャンネルチャットの展開準備 - -チャンネルチャットを展開するには,LunaticChat の設定ファイル `config.yml` でチャンネルチャット機能を有効化する必要があります. - -1. サーバーを停止します. -2. `plugins/LunaticChat/config.yml` を開きます. -3. `features.channelChat.enabled` を `true` に設定します. -4. サーバーを再起動します. - -以上でチャンネルチャット機能が有効化され,プレイヤーがチャンネルチャットを使用できるようになります. - -## チャンネルチャットの設定 - -チャンネルチャットに関する設定項目は以下の通りです. - -- `features.channelChat.maxChannelsPerPlayer`: 1人のプレイヤーが作成できるチャンネルの最大数を指定します. -- `features.channelChat.maxMembersPerChannel`: 1つのチャンネルに参加できるメンバーの最大数を指定します. -- `features.channelChat.maxMembershipPerPlayer`: 1人のプレイヤーが参加できるチャンネルの最大数を指定します. - -デフォルトは `0` に設定されており,制限はありません. - -::: tip 設定のおすすめは? - -チャンネルチャット機能を活発に使用したい場合は,これらの値を高めに設定することをお勧めします. - -ただし,サーバーのパフォーマンスに影響を与える可能性があるため,サーバーのリソース状況に応じて適切に設定してください. - -おすすめの設定は次のとおりです. - -- `features.channelChat.maxChannelsPerPlayer`: `3` 〜 `5` -- `features.channelChat.maxMembersPerChannel`: `20` 〜 `50` -- `features.channelChat.maxMembershipPerPlayer`: `5` 〜 `10` - -::: - -## チャンネルの管理 - -基本的に,[チャンネルの管理はプレイヤー自身が行います](../player/channel-chat/moderation.md). - -ただし,サーバー管理者として,以下の点に注意してください: - -- サーバー管理者はすべてのチャンネルに対してオーナー権限を持ちます.操作が必要な場合は,適切に対応してください. -- サーバーのパフォーマンスを維持するために,必要に応じてチャンネル数やメンバー数の制限を設定してください. -- 不適切なチャンネルやメンバー行動が発生した場合は,適切な措置を講じてください. - -また,それらのチャンネルの管理によるトラブルを回避したい場合は `/lc channel ban` などのモデレートコマンドの制限を検討してください. diff --git a/docs/src/guide/admin/management-data.md b/docs/src/guide/admin/management-data.md new file mode 100644 index 0000000..5527ffa --- /dev/null +++ b/docs/src/guide/admin/management-data.md @@ -0,0 +1,107 @@ +# データ・ログ + +::: danger 編集厳禁 + +これらのデータファイルは LunaticChat の動作に不可欠です.直接編集すると,データの破損や予期せぬ動作を引き起こす可能性があります.データのバックアップを取る場合を除き,これらのファイルを直接編集しないでください. + +::: + +## データの保存場所 + +LunaticChat は、チャンネルデータや設定情報をローカルディスクに保存します. + +- `channels.json`: チャンネル情報を保存するファイルです. +- `chatmodes.json`: プレイヤーのチャットモード設定を保存するファイルです. +- `conversion_cache.json`: チャンネル変換のキャッシュを保存するファイルです. +- `player-settings.yaml`: プレイヤーごとの設定情報を保存するファイルです. + +::: tip 定期的なバックアップ + +LunaticChat のデータの安全性を確保するために,定期的にバックアップを作成することをお勧めします. + +::: + +### `channels.json` + +`channels.json` ファイルは、LunaticChat が管理するチャンネルの情報を保存します.このファイルには,チャンネル名、参加者リスト、チャットモードなどの情報が含まれます. + +```json +{ + "channels": { + "general-channel": { + "id": "general-channel", + "name": "一般チャンネル", + "ownerId": "a01e3843-e521-3998-958a-f459800e4d11", + "createdAt": 1769507213150, + "bannedPlayers": [ + "ceaea267-39dd-3bac-931c-761ada671ebe" + ] + } + }, + "members": { + "general-channel": [ + { + "channelId": "test2", + "playerId": "a01e3843-e521-3998-958a-f459800e4d11", + "role": "OWNER", + "joinedAt": 1769507213150 + } + ] + }, + "activeChannels": { + "a01e3843-e521-3998-958a-f459800e4d11": "test2" + } +} +``` + +### `chatmodes.json` + +`chatmodes.json` ファイルは、プレイヤーのチャットモード設定を保存します.このファイルには,プレイヤーごとのチャットモード情報が含まれます. + +```json +{ + "modes": { + "aed5efd4-551b-3965-bc28-ae21aa072a66": "CHANNEL", + "ceaea267-39dd-3bac-931c-761ada671ebe": "CHANNEL", + "a01e3843-e521-3998-958a-f459800e4d11": "CHANNEL", + "681f539b-8bb8-3f85-85e5-a2945f6c6539": "GLOBAL" + } +} +``` + +### `conversion_cache.json` + +`conversion_cache.json` ファイルは、チャンネル変換のキャッシュを保存します.このファイルには,プレイヤーごとのチャンネル変換情報が含まれます. + +キャッシュシステムに関する詳細は [こちら](./cache.md) をご覧ください. + + +```json +{"version":"1","entries":{"hi":"日"}} +``` + +### `player-settings.yaml` + +`player-settings.yaml` ファイルは、プレイヤーごとの設定情報を保存します.このファイルには,プレイヤーの個別設定が含まれます. + +```yaml +version: 1 +japaneseConversion: + "aed5efd4-551b-3965-bc28-ae21aa072a66": false + "ceaea267-39dd-3bac-931c-761ada671ebe": false +directMessageNotification: + "aed5efd4-551b-3965-bc28-ae21aa072a66": true + "ceaea267-39dd-3bac-931c-761ada671ebe": true +channelMessageNotification: + "ceaea267-39dd-3bac-931c-761ada671ebe": true +``` + +## キャッシュバージョン + +ディスクキャッシュに使用されるファイルには `version` フィールドが含まれており,LunaticChat のバージョンアップに伴うキャッシュフォーマットの変更に対応しています. + +バージョンが不一致の場合,LunaticChat はキャッシュファイルを **古い形式のキャッシュ** として認識し,内容を無視して新しい形式で再作成します. + +```json +{"version":"1","entries":{}} +``` diff --git a/docs/src/index.md b/docs/src/index.md index 430077f..c3410cb 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -32,9 +32,6 @@ features: - title: かな・ローマ字変換 details: ローマ字で入力したメッセージを,自動的に日本語に変換 icon: 🌍 - - title: CoreProtect への対応 - details: LunaticChat のチャットログは CoreProtect と互換性があります - icon: 🗒️ - title: チャンネルチャット機能 details: チャットチャンネルを作成・管理し,特定のプレイヤー間でのプライベートメッセージを送信可能 icon: ☎️ diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/ServiceInitializer.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/ServiceInitializer.kt index e6ea864..70495a4 100644 --- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/ServiceInitializer.kt +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/ServiceInitializer.kt @@ -240,6 +240,7 @@ class ServiceInitializer( io.ktor.util.logging .KtorSimpleLogger("ChannelMessageLogger"), maxFileSizeBytes = configuration.features.channelChat.messageLogging.maxFileSizeMB * 1024L * 1024L, + retentionDays = configuration.features.channelChat.messageLogging.retentionDays, ).also { channelMessageLogger = it logger.info( @@ -299,26 +300,6 @@ class ServiceInitializer( saveInterval, ) } - - // Schedule cleanup of old channel message logs - if (configuration.features.channelChat.messageLogging.enabled && - configuration.features.channelChat.messageLogging.retentionDays > 0 && - channelMessageLogger != null - ) { - val cleanupInterval = 24 * 60 * 60 * 20L // 24 hours in ticks - val initialDelay = 5 * 60 * 20L // 5 minutes after startup - - plugin.server.scheduler.runTaskTimerAsynchronously( - plugin, - Runnable { - channelMessageLogger?.cleanupOldLogs( - configuration.features.channelChat.messageLogging.retentionDays, - ) - }, - initialDelay, - cleanupInterval, - ) - } } /** @@ -329,6 +310,6 @@ class ServiceInitializer( conversionCache?.saveToDisk() services.channelManager?.saveToDisk() services.chatModeManager?.shutdown() - channelMessageLogger?.flushSync() + channelMessageLogger?.shutdown() } } diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMessageLogger.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMessageLogger.kt index d14b097..206968c 100644 --- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMessageLogger.kt +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMessageLogger.kt @@ -28,22 +28,27 @@ import kotlin.io.path.name * @property plugin Bukkit plugin instance for scheduling tasks * @property logger Logger for diagnostic messages * @property maxFileSizeBytes Maximum size of a single log file + * @property retentionDays Number of days to retain log files (0 = keep forever) */ class ChannelMessageLogger( private val logsDirectory: Path, private val plugin: Plugin, private val logger: Logger, private val maxFileSizeBytes: Long, + private val retentionDays: Int, ) { private val pendingEntries = ConcurrentLinkedQueue() private val json = Json { encodeDefaults = true } private var flushTaskId: Int? = null + private var cleanupTaskId: Int? = null companion object { private const val LOG_FILE_PREFIX = "channel-messages-" private const val LOG_FILE_EXTENSION = ".json" private val DATE_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") private const val FLUSH_INTERVAL_TICKS = 20L // 1 second + private const val CLEANUP_INTERVAL_TICKS = 24 * 60 * 60 * 20L // 24 hours + private const val CLEANUP_INITIAL_DELAY_TICKS = 5 * 60 * 20L // 5 minutes } init { @@ -52,6 +57,7 @@ class ChannelMessageLogger( Files.createDirectories(logsDirectory) logger.info("Channel message logger initialized at: $logsDirectory") schedulePeriodicFlush() + schedulePeriodicCleanup() } catch (e: Exception) { logger.error("Failed to initialize channel message logger", e) } @@ -80,9 +86,35 @@ class ChannelMessageLogger( ).taskId } + /** + * Schedules periodic cleanup of old log files. + */ + private fun schedulePeriodicCleanup() { + if (retentionDays <= 0) { + logger.info("Log retention disabled (retentionDays = $retentionDays)") + return + } + + cleanupTaskId = + plugin.server.scheduler + .runTaskTimerAsynchronously( + plugin, + Runnable { cleanupOldLogs(retentionDays) }, + CLEANUP_INITIAL_DELAY_TICKS, + CLEANUP_INTERVAL_TICKS, + ).taskId + + logger.info("Scheduled log cleanup task (retention: $retentionDays days)") + } + /** * Flushes all pending entries to the current day's log file. + * Automatically creates new files with suffixes when size limit is exceeded. + * + * This method is synchronized to prevent race conditions between periodic + * flush operations and shutdown flush. */ + @Synchronized private fun flushPendingEntries() { if (pendingEntries.isEmpty()) { return @@ -101,12 +133,6 @@ class ChannelMessageLogger( try { val logFile = getCurrentLogFile() - // Check file size before writing - if (Files.exists(logFile) && logFile.fileSize() >= maxFileSizeBytes) { - logger.warn("Log file ${logFile.name} exceeded maximum size, skipping flush") - return - } - BufferedWriter( Files.newBufferedWriter( logFile, @@ -128,20 +154,22 @@ class ChannelMessageLogger( } /** - * Synchronously flushes all pending entries. + * Shuts down the logger by cancelling scheduled tasks and flushing pending entries. * Should be called during plugin shutdown. */ - fun flushSync() { - // Cancel scheduled task + fun shutdown() { + // Cancel scheduled tasks flushTaskId?.let { plugin.server.scheduler.cancelTask(it) } + cleanupTaskId?.let { plugin.server.scheduler.cancelTask(it) } // Flush remaining entries flushPendingEntries() - logger.info("Channel message logger flushed all pending entries") + logger.info("Channel message logger shut down (flushed all pending entries)") } /** * Deletes log files older than the specified retention period. + * Handles both base files (YYYY-MM-DD.json) and suffixed files (YYYY-MM-DD-N.json). * * @param retentionDays Number of days to retain log files */ @@ -154,20 +182,28 @@ class ChannelMessageLogger( val cutoffDate = LocalDate.now(ZoneOffset.UTC).minusDays(retentionDays.toLong()) val logFiles = logsDirectory.listDirectoryEntries("$LOG_FILE_PREFIX*$LOG_FILE_EXTENSION") + // Pattern: channel-messages-YYYY-MM-DD(-N)?.json + val datePattern = Regex("""${Regex.escape(LOG_FILE_PREFIX)}(\d{4}-\d{2}-\d{2})(?:-\d+)?${Regex.escape(LOG_FILE_EXTENSION)}""") + var deletedCount = 0 for (logFile in logFiles) { val fileName = logFile.name - val dateStr = fileName.removePrefix(LOG_FILE_PREFIX).removeSuffix(LOG_FILE_EXTENSION) - - try { - val fileDate = LocalDate.parse(dateStr, DATE_FORMATTER) - if (fileDate.isBefore(cutoffDate)) { - logFile.deleteIfExists() - deletedCount++ - logger.info("Deleted old log file: $fileName") + val matchResult = datePattern.matchEntire(fileName) + + if (matchResult != null) { + val dateStr = matchResult.groupValues[1] + try { + val fileDate = LocalDate.parse(dateStr, DATE_FORMATTER) + if (fileDate.isBefore(cutoffDate)) { + logFile.deleteIfExists() + deletedCount++ + logger.info("Deleted old log file: $fileName") + } + } catch (e: Exception) { + logger.warn("Failed to parse date from log file: $fileName", e) } - } catch (e: Exception) { - logger.warn("Failed to parse date from log file: $fileName", e) + } else { + logger.warn("Log file name does not match expected pattern: $fileName") } } @@ -181,11 +217,31 @@ class ChannelMessageLogger( /** * Gets the log file path for the current UTC date. + * If the current file exceeds the size limit, returns a new file with a suffix. + * Filenames follow the pattern: channel-messages-YYYY-MM-DD(-N).json */ private fun getCurrentLogFile(): Path { val currentDate = LocalDate.now(ZoneOffset.UTC) val dateStr = currentDate.format(DATE_FORMATTER) - val fileName = "$LOG_FILE_PREFIX$dateStr$LOG_FILE_EXTENSION" - return logsDirectory.resolve(fileName) + + // Try base filename first + var fileName = "$LOG_FILE_PREFIX$dateStr$LOG_FILE_EXTENSION" + var logFile = logsDirectory.resolve(fileName) + + // If file exists and exceeds size limit, find next available suffix + var suffix = 1 + while (Files.exists(logFile) && logFile.fileSize() >= maxFileSizeBytes) { + fileName = "$LOG_FILE_PREFIX$dateStr-$suffix$LOG_FILE_EXTENSION" + logFile = logsDirectory.resolve(fileName) + suffix++ + + // Safety limit to prevent infinite loop + if (suffix > 1000) { + logger.error("Too many log files for date $dateStr (limit: 1000), using latest") + break + } + } + + return logFile } } diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt index 861707a..03423e5 100644 --- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt +++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/listener/PlayerChatListener.kt @@ -26,7 +26,7 @@ class PlayerChatListener( ) : Listener { private val plainTextSerializer = PlainTextComponentSerializer.plainText() - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) fun onChat(event: AsyncChatEvent) { val player = event.player val settings = settingsManager.getSettings(player.uniqueId) -- cgit v1.2.1