From e3e296bd5e01f21d5648ae952b783a64ca46d24a Mon Sep 17 00:00:00 2001 From: jie65535 Date: Sat, 8 Aug 2026 14:21:32 +0800 Subject: [PATCH] fix: refresh meme prompt on reload --- src/main/kotlin/command/PluginCommands.kt | 2 + .../conversation/ConversationContext.kt | 64 ++++++++++++------- .../conversation/ConversationContextTest.kt | 22 +++++++ 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/command/PluginCommands.kt b/src/main/kotlin/command/PluginCommands.kt index 174c4b4..9eb3c0c 100644 --- a/src/main/kotlin/command/PluginCommands.kt +++ b/src/main/kotlin/command/PluginCommands.kt @@ -18,6 +18,7 @@ import top.jie65535.mirai.JChatGPT.reload import top.jie65535.mirai.config.PluginConfig import top.jie65535.mirai.config.ModelConfig import top.jie65535.mirai.config.ModelConfigMigration +import top.jie65535.mirai.conversation.ConversationContext import top.jie65535.mirai.data.PluginData import top.jie65535.mirai.data.SkillStore import top.jie65535.mirai.data.TokenUsageStore @@ -58,6 +59,7 @@ object PluginCommands : CompositeCommand( ProfileAutoMaintenance.clear() } SkillStore.reload() + ConversationContext.invalidateMemePromptCache() sendMessage("OK") } diff --git a/src/main/kotlin/conversation/ConversationContext.kt b/src/main/kotlin/conversation/ConversationContext.kt index 7cc2c78..5d17972 100644 --- a/src/main/kotlin/conversation/ConversationContext.kt +++ b/src/main/kotlin/conversation/ConversationContext.kt @@ -72,6 +72,39 @@ internal class ReplyIndex { fun indexOfIds(ids: String): Int? = indexByIds[ids] } +internal class MemePromptCache { + private var prompt: String? = null + + fun get(directoryPath: String): String = synchronized(this) { + prompt ?: buildPrompt(directoryPath).also { prompt = it } + } + + fun clear() { + synchronized(this) { prompt = null } + } + + private fun buildPrompt(directoryPath: String): String { + if (directoryPath.isEmpty()) return "" + return buildString { + val directory = File(directoryPath) + if (!directory.isDirectory) { + append("配置的meme路径不存在!") + return@buildString + } + append("memes文件夹地址为:").appendLine(directoryPath) + val memes = directory.list().orEmpty() + if (memes.isEmpty()) { + append("暂无表情包~") + } else { + memes.forEach { append("- ").appendLine(it) } + appendLine() + append("表情包示例:![").append(memes[0]).append("](") + .append(File(directory, memes[0]).absoluteFile).appendLine(")") + } + } + } +} + internal object ConversationContext { private val chronologicalRecordOrder = compareBy { it.time } .thenBy { if (it.id == 0L) Long.MAX_VALUE else it.id } @@ -81,16 +114,21 @@ internal object ConversationContext { private val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd E HH:mm:ss") private val shortTimeFormatter = DateTimeFormatter.ofPattern("HH:mm") .withZone(ZoneOffset.systemDefault()) - private var memePrompt: String? = null + private val memePromptCache = MemePromptCache() fun clearCache() { synchronized(contextCache) { contextCache.clear() } } + fun invalidateMemePromptCache() { + memePromptCache.clear() + } + fun clearAll() { synchronized(contextCache) { contextCache.clear() } synchronized(replyIndexes) { replyIndexes.clear() } synchronized(imageIndexes) { imageIndexes.clear() } + memePromptCache.clear() } fun cache(subjectId: Long): ConversationCache? = synchronized(contextCache) { @@ -150,7 +188,7 @@ internal object ConversationContext { replace("{skills}") { if (PluginConfig.skillsEnabled) SkillStore.buildIndexPrompt() else "暂无技能" } - replace("{meme}") { buildMemePrompt() } + replace("{meme}") { memePromptCache.get(PluginConfig.memeDir) } return prompt.toString() } @@ -258,28 +296,6 @@ internal object ConversationContext { imageIndexes.getOrPut(subjectId) { ImageIndex() } } - private fun buildMemePrompt(): String { - memePrompt?.let { return it } - if (PluginConfig.memeDir.isEmpty()) return "" - return buildString { - val directory = File(PluginConfig.memeDir) - if (!directory.isDirectory) { - append("配置的meme路径不存在!") - return@buildString - } - append("memes文件夹地址为:").appendLine(PluginConfig.memeDir) - val memes = directory.list().orEmpty() - if (memes.isEmpty()) { - append("暂无表情包~") - } else { - memes.forEach { append("- ").appendLine(it) } - appendLine() - append("表情包示例:![").append(memes[0]).append("](") - .append(File(directory, memes[0]).absoluteFile).appendLine(")") - } - }.also { memePrompt = it } - } - private fun appendUserProfileContext( target: StringBuilder, history: List, diff --git a/src/test/kotlin/conversation/ConversationContextTest.kt b/src/test/kotlin/conversation/ConversationContextTest.kt index 4ac4333..5a9521c 100644 --- a/src/test/kotlin/conversation/ConversationContextTest.kt +++ b/src/test/kotlin/conversation/ConversationContextTest.kt @@ -1,9 +1,12 @@ package top.jie65535.mirai.conversation +import java.nio.file.Files import net.mamoe.mirai.message.data.MessageSourceKind import top.jie65535.mirai.data.ChatMessageRecord import kotlin.test.Test +import kotlin.test.assertContains import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertNull class ConversationContextTest { @@ -22,6 +25,25 @@ class ConversationContextTest { assertNull(index.indexOfIds("missing")) } + @Test + fun memePromptCacheRefreshesDirectoryListingAfterClear() { + val directory = Files.createTempDirectory("jchatgpt-meme-prompt-test-") + try { + Files.createFile(directory.resolve("before.png")) + val cache = MemePromptCache() + + assertContains(cache.get(directory.toString()), "before.png") + + Files.createFile(directory.resolve("after.png")) + assertFalse(cache.get(directory.toString()).contains("after.png")) + + cache.clear() + assertContains(cache.get(directory.toString()), "after.png") + } finally { + directory.toFile().deleteRecursively() + } + } + private fun record(ids: String?, time: Int) = ChatMessageRecord( botId = 1, fromId = 2,