diff --git a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/DefaultMessageContentCache.kt b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/DefaultMessageContentCache.kt index 71c6de5..755e430 100644 --- a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/DefaultMessageContentCache.kt +++ b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/DefaultMessageContentCache.kt @@ -7,20 +7,18 @@ import dev.inmo.tgbotapi.requests.get.GetFile import dev.inmo.tgbotapi.requests.send.media.* import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.InputMedia.* -import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent import dev.inmo.tgbotapi.utils.asInput -import io.ktor.utils.io.cancel import io.ktor.utils.io.core.Input -class DefaultMessageContentCache( +class DefaultMessageContentCache( private val bot: TelegramBot, private val filesRefreshingChatId: ChatId, - private val simpleMessageContentCache: MessagesSimpleCache = InMemoryMessagesSimpleCache(), - private val messagesFilesCache: MessagesFilesCache = InMemoryMessagesFilesCache() -) : MessageContentCache { - override suspend fun save(chatId: ChatId, messageId: MessageIdentifier, content: MessageContent): Boolean { + private val simpleMessageContentCache: MessagesSimpleCache, + private val messagesFilesCache: MessagesFilesCache = InMemoryMessagesFilesCache() +) : MessageContentCache { + override suspend fun save(content: MessageContent): K { return when (content) { is MediaContent -> { val extendedInfo = bot.execute( @@ -32,42 +30,37 @@ class DefaultMessageContentCache( ) ) - save(chatId, messageId, content, extendedInfo.fileName) { + save(content, extendedInfo.fileName) { allocator.invoke().asInput() } } - else -> simpleMessageContentCache.runCatching { - set(chatId, messageId, content) - }.isSuccess + else -> simpleMessageContentCache.add(content) } } override suspend fun save( - chatId: ChatId, - messageId: MessageIdentifier, content: MediaContent, filename: String, inputAllocator: suspend () -> Input - ): Boolean { + ): K { + val key = simpleMessageContentCache.add(content) runCatching { - messagesFilesCache.set(chatId, messageId, filename, inputAllocator) + messagesFilesCache.set(key, filename, inputAllocator) }.onFailure { - return false + simpleMessageContentCache.remove(key) } - return simpleMessageContentCache.runCatching { - set(chatId, messageId, content) - }.isSuccess + return key } - override suspend fun get(chatId: ChatId, messageId: MessageIdentifier): MessageContent? { - val savedSimpleContent = simpleMessageContentCache.get(chatId, messageId) ?: return null + override suspend fun get(k: K): MessageContent? { + val savedSimpleContent = simpleMessageContentCache.get(k) ?: return null if (savedSimpleContent is MediaContent) { runCatching { bot.execute(GetFile(savedSimpleContent.media.fileId)) }.onFailure { - val savedFileContentAllocator = messagesFilesCache.get(chatId, messageId) ?: error("Unexpected absence of $chatId:$messageId file for content ($simpleMessageContentCache)") + val savedFileContentAllocator = messagesFilesCache.get(k) ?: error("Unexpected absence of $k file for content ($simpleMessageContentCache)") val newContent = bot.execute( when (savedSimpleContent.asInputMedia()) { is InputMediaAnimation -> SendAnimation( @@ -108,19 +101,28 @@ class DefaultMessageContentCache( } ) - simpleMessageContentCache.set(chatId, messageId, newContent.content) + simpleMessageContentCache.update(k, newContent.content) return newContent.content } } return savedSimpleContent } - override suspend fun contains(chatId: ChatId, messageId: MessageIdentifier): Boolean { - return simpleMessageContentCache.contains(chatId, messageId) + override suspend fun contains(k: K): Boolean { + return simpleMessageContentCache.contains(k) } - override suspend fun remove(chatId: ChatId, messageId: MessageIdentifier) { - simpleMessageContentCache.remove(chatId, messageId) - messagesFilesCache.remove(chatId, messageId) + override suspend fun remove(k: K) { + simpleMessageContentCache.remove(k) + messagesFilesCache.remove(k) + } + + companion object { + operator fun invoke( + bot: TelegramBot, + filesRefreshingChatId: ChatId, + simpleMessageContentCache: MessagesSimpleCache = InMemoryMessagesSimpleCache(), + messagesFilesCache: MessagesFilesCache = InMemoryMessagesFilesCache() + ) = DefaultMessageContentCache(bot, filesRefreshingChatId, simpleMessageContentCache, messagesFilesCache) } } diff --git a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessageContentCache.kt b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessageContentCache.kt index 2718920..baec180 100644 --- a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessageContentCache.kt +++ b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessageContentCache.kt @@ -1,21 +1,17 @@ package dev.inmo.tgbotapi.libraries.cache.media.common -import dev.inmo.tgbotapi.types.ChatId -import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent import io.ktor.utils.io.core.Input -interface MessageContentCache { - suspend fun save(chatId: ChatId, messageId: MessageIdentifier, content: MessageContent): Boolean +interface MessageContentCache { + suspend fun save(content: MessageContent): K suspend fun save( - chatId: ChatId, - messageId: MessageIdentifier, content: MediaContent, filename: String, inputAllocator: suspend () -> Input - ): Boolean - suspend fun get(chatId: ChatId, messageId: MessageIdentifier): MessageContent? - suspend fun contains(chatId: ChatId, messageId: MessageIdentifier): Boolean - suspend fun remove(chatId: ChatId, messageId: MessageIdentifier) + ): K + suspend fun get(k: K): MessageContent? + suspend fun contains(k: K): Boolean + suspend fun remove(k: K) } diff --git a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessagesFilesCache.kt b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessagesFilesCache.kt index a23fa5d..796fca9 100644 --- a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessagesFilesCache.kt +++ b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessagesFilesCache.kt @@ -5,16 +5,11 @@ import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.utils.StorageFile import io.ktor.utils.io.core.* -interface MessagesFilesCache { - suspend fun set( - chatId: ChatId, - messageIdentifier: MessageIdentifier, - filename: String, - inputAllocator: suspend () -> Input - ) - suspend fun get(chatId: ChatId, messageIdentifier: MessageIdentifier): StorageFile? - suspend fun remove(chatId: ChatId, messageIdentifier: MessageIdentifier) - suspend fun contains(chatId: ChatId, messageIdentifier: MessageIdentifier): Boolean +interface MessagesFilesCache { + suspend fun set(k: K, filename: String, inputAllocator: suspend () -> Input) + suspend fun get(k: K): StorageFile? + suspend fun remove(k: K) + suspend fun contains(k: K): Boolean } /** @@ -22,31 +17,25 @@ interface MessagesFilesCache { * start of application creation with usage of [MessageContentCache] with aim to replace this realization by some * disks-oriented one */ -class InMemoryMessagesFilesCache : MessagesFilesCache { - private val map = mutableMapOf, StorageFile>() +class InMemoryMessagesFilesCache : MessagesFilesCache { + private val map = mutableMapOf() - override suspend fun set( - chatId: ChatId, - messageIdentifier: MessageIdentifier, - filename: String, - inputAllocator: suspend () -> Input - ) { - map[chatId to messageIdentifier] = StorageFile( + override suspend fun set(k: K, filename: String, inputAllocator: suspend () -> Input) { + map[k] = StorageFile( filename, inputAllocator().readBytes() ) } - override suspend fun get(chatId: ChatId, messageIdentifier: MessageIdentifier): StorageFile? { - return map[chatId to messageIdentifier] + override suspend fun get(k: K): StorageFile? { + return map[k] } - override suspend fun remove(chatId: ChatId, messageIdentifier: MessageIdentifier) { - map.remove(chatId to messageIdentifier) + override suspend fun remove(k: K) { + map.remove(k) } - override suspend fun contains(chatId: ChatId, messageIdentifier: MessageIdentifier): Boolean { - return map.contains(chatId to messageIdentifier) + override suspend fun contains(k: K): Boolean { + return map.contains(k) } - } diff --git a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessagesSimpleCache.kt b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessagesSimpleCache.kt index a325fe2..ffc08b1 100644 --- a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessagesSimpleCache.kt +++ b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MessagesSimpleCache.kt @@ -1,20 +1,14 @@ package dev.inmo.tgbotapi.libraries.cache.media.common -import dev.inmo.tgbotapi.types.ChatId -import dev.inmo.tgbotapi.types.MessageIdentifier +import com.benasher44.uuid.uuid4 import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent -import dev.inmo.tgbotapi.utils.StorageFile -import io.ktor.utils.io.core.* -interface MessagesSimpleCache { - suspend fun set( - chatId: ChatId, - messageIdentifier: MessageIdentifier, - content: MessageContent - ) - suspend fun get(chatId: ChatId, messageIdentifier: MessageIdentifier): MessageContent? - suspend fun remove(chatId: ChatId, messageIdentifier: MessageIdentifier) - suspend fun contains(chatId: ChatId, messageIdentifier: MessageIdentifier): Boolean +interface MessagesSimpleCache { + suspend fun add(content: MessageContent): K + suspend fun update(k: K, content: MessageContent): Boolean + suspend fun get(k: K): MessageContent? + suspend fun remove(k: K) + suspend fun contains(k: K): Boolean } /** @@ -22,27 +16,48 @@ interface MessagesSimpleCache { * start of application creation with usage of [MessageContentCache] with aim to replace this realization by some * disks-oriented one */ -class InMemoryMessagesSimpleCache : MessagesSimpleCache { - private val map = mutableMapOf, MessageContent>() +class InMemoryMessagesSimpleCache( + private val keyGenerator: () -> K +) : MessagesSimpleCache { + private val map = mutableMapOf() - override suspend fun set( - chatId: ChatId, - messageIdentifier: MessageIdentifier, + override suspend fun add( content: MessageContent - ) { - map[chatId to messageIdentifier] = content + ): K { + val key = keyGenerator() + map[key] = content + return key } - override suspend fun get(chatId: ChatId, messageIdentifier: MessageIdentifier): MessageContent? { - return map[chatId to messageIdentifier] + override suspend fun update( + k: K, + content: MessageContent + ): Boolean { + return map.runCatching { + if (contains(k)) { + put(k, content) + true + } else { + false + } + }.getOrDefault(false) } - override suspend fun remove(chatId: ChatId, messageIdentifier: MessageIdentifier) { - map.remove(chatId to messageIdentifier) + override suspend fun get(k: K): MessageContent? { + return map[k] } - override suspend fun contains(chatId: ChatId, messageIdentifier: MessageIdentifier): Boolean { - return map.contains(chatId to messageIdentifier) + override suspend fun remove(k: K) { + map.remove(k) } + override suspend fun contains(k: K): Boolean { + return map.contains(k) + } + + companion object { + operator fun invoke() = InMemoryMessagesSimpleCache { + uuid4().toString() + } + } } diff --git a/cache/content/common/src/jvmMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/InFilesMessagesFilesCache.kt b/cache/content/common/src/jvmMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/InFilesMessagesFilesCache.kt index 9244e6c..53aed1c 100644 --- a/cache/content/common/src/jvmMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/InFilesMessagesFilesCache.kt +++ b/cache/content/common/src/jvmMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/InFilesMessagesFilesCache.kt @@ -1,7 +1,5 @@ package dev.inmo.tgbotapi.libraries.cache.media.common -import dev.inmo.tgbotapi.types.ChatId -import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.utils.* import io.ktor.utils.io.core.Input import io.ktor.utils.io.core.copyTo @@ -9,12 +7,13 @@ import io.ktor.utils.io.streams.asInput import io.ktor.utils.io.streams.asOutput import java.io.File -class InFilesMessagesFilesCache( - private val folderFile: File -) : MessagesFilesCache { - private val Pair.storageFile: StorageFile? +class InFilesMessagesFilesCache( + private val folderFile: File, + private val filePrefixBuilder: (K) -> String +) : MessagesFilesCache { + private val K.storageFile: StorageFile? get() { - val prefix = filePrefix(first, second) + val prefix = filePrefix(this) val filename = folderFile.list() ?.firstOrNull { it.startsWith(prefix) } ?: return null val file = File(folderFile, filename) val storageFileFilename = file.name.removePrefix("$prefix ") @@ -31,21 +30,14 @@ class InFilesMessagesFilesCache( folderFile.mkdirs() } - private fun filePrefix(chatId: ChatId, messageIdentifier: MessageIdentifier): String { - return "${chatId.chatId} $messageIdentifier" + private fun filePrefix(k: K): String = filePrefixBuilder(k) + + private fun fileName(k: K, filename: String): String { + return "${filePrefix(k)} $filename" } - private fun fileName(chatId: ChatId, messageIdentifier: MessageIdentifier, filename: String): String { - return "${chatId.chatId} $messageIdentifier $filename" - } - - override suspend fun set( - chatId: ChatId, - messageIdentifier: MessageIdentifier, - filename: String, - inputAllocator: suspend () -> Input - ) { - val fullFileName = fileName(chatId, messageIdentifier, filename) + override suspend fun set(k: K, filename: String, inputAllocator: suspend () -> Input) { + val fullFileName = fileName(k, filename) val file = File(folderFile, fullFileName).apply { delete() } @@ -56,12 +48,12 @@ class InFilesMessagesFilesCache( } } - override suspend fun get(chatId: ChatId, messageIdentifier: MessageIdentifier): StorageFile? { - return (chatId to messageIdentifier).storageFile + override suspend fun get(k: K): StorageFile? { + return k.storageFile } - override suspend fun remove(chatId: ChatId, messageIdentifier: MessageIdentifier) { - val prefix = filePrefix(chatId, messageIdentifier) + override suspend fun remove(k: K) { + val prefix = filePrefix(k) folderFile.listFiles() ?.forEach { if (it.name.startsWith(prefix)) { it.delete() @@ -69,8 +61,14 @@ class InFilesMessagesFilesCache( } } - override suspend fun contains(chatId: ChatId, messageIdentifier: MessageIdentifier): Boolean { - val prefix = filePrefix(chatId, messageIdentifier) + override suspend fun contains(k: K): Boolean { + val prefix = filePrefix(k) return folderFile.list() ?.any { it.startsWith(prefix) } == true } + + companion object { + operator fun invoke(folderFile: File) = InFilesMessagesFilesCache( + folderFile + ) { it } + } } diff --git a/cache/content/micro_utils/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/micro_utils/SimpleKeyValueMessageContentCache.kt b/cache/content/micro_utils/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/micro_utils/SimpleKeyValueMessageContentCache.kt index 0cf0ce0..e77a7db 100644 --- a/cache/content/micro_utils/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/micro_utils/SimpleKeyValueMessageContentCache.kt +++ b/cache/content/micro_utils/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/micro_utils/SimpleKeyValueMessageContentCache.kt @@ -1,5 +1,6 @@ package dev.inmo.tgbotapi.libraries.cache.media.micro_utils +import com.benasher44.uuid.uuid4 import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.mappers.withMapper import dev.inmo.tgbotapi.libraries.cache.media.common.MessagesSimpleCache @@ -14,23 +15,44 @@ import kotlinx.serialization.modules.SerializersModule import kotlin.js.JsName import kotlin.jvm.JvmName -class SimpleKeyValueMessageContentCache( - private val keyValueRepo: KeyValueRepo, MessageContent> -) : MessagesSimpleCache { - override suspend fun set(chatId: ChatId, messageIdentifier: MessageIdentifier, content: MessageContent) { - keyValueRepo.set(chatId to messageIdentifier, content) +class SimpleKeyValueMessageContentCache( + private val keyValueRepo: KeyValueRepo, + private val keyGenerator: () -> K +) : MessagesSimpleCache { + override suspend fun add(content: MessageContent): K { + val key = keyGenerator() + keyValueRepo.set(key, content) + + return key } - override suspend fun get(chatId: ChatId, messageIdentifier: MessageIdentifier): MessageContent? { - return keyValueRepo.get(chatId to messageIdentifier) + override suspend fun update(k: K, content: MessageContent): Boolean { + return keyValueRepo.runCatching { + if (contains(k)) { + keyValueRepo.set(k, content) + true + } else { + false + } + }.getOrDefault(false) } - override suspend fun contains(chatId: ChatId, messageIdentifier: MessageIdentifier): Boolean { - return keyValueRepo.contains(chatId to messageIdentifier) + override suspend fun get(k: K): MessageContent? { + return keyValueRepo.get(k) } - override suspend fun remove(chatId: ChatId, messageIdentifier: MessageIdentifier) { - keyValueRepo.unset(chatId to messageIdentifier) + override suspend fun contains(k: K): Boolean { + return keyValueRepo.contains(k) + } + + override suspend fun remove(k: K) { + keyValueRepo.unset(k) + } + + companion object { + operator fun invoke( + keyValueRepo: KeyValueRepo + ) = SimpleKeyValueMessageContentCache(keyValueRepo) { uuid4().toString() } } }