mirror of
				https://github.com/InsanusMokrassar/TelegramBotApiLibraries.git
				synced 2025-11-03 21:52:28 +00:00 
			
		
		
		
	add opportunity to use custom keys
This commit is contained in:
		@@ -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<K>(
 | 
			
		||||
    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<K>,
 | 
			
		||||
    private val messagesFilesCache: MessagesFilesCache<K> = InMemoryMessagesFilesCache()
 | 
			
		||||
) : MessageContentCache<K> {
 | 
			
		||||
    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<String> = InMemoryMessagesSimpleCache(),
 | 
			
		||||
            messagesFilesCache: MessagesFilesCache<String> = InMemoryMessagesFilesCache()
 | 
			
		||||
        ) = DefaultMessageContentCache(bot, filesRefreshingChatId, simpleMessageContentCache, messagesFilesCache)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<K> {
 | 
			
		||||
    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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<K> {
 | 
			
		||||
    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<Pair<ChatId, MessageIdentifier>, StorageFile>()
 | 
			
		||||
class InMemoryMessagesFilesCache<K> : MessagesFilesCache<K> {
 | 
			
		||||
    private val map = mutableMapOf<K, StorageFile>()
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<K> {
 | 
			
		||||
    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<Pair<ChatId, MessageIdentifier>, MessageContent>()
 | 
			
		||||
class InMemoryMessagesSimpleCache<K>(
 | 
			
		||||
    private val keyGenerator: () -> K
 | 
			
		||||
) : MessagesSimpleCache<K> {
 | 
			
		||||
    private val map = mutableMapOf<K, MessageContent>()
 | 
			
		||||
 | 
			
		||||
    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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<ChatId, MessageIdentifier>.storageFile: StorageFile?
 | 
			
		||||
class InFilesMessagesFilesCache<K>(
 | 
			
		||||
    private val folderFile: File,
 | 
			
		||||
    private val filePrefixBuilder: (K) -> String
 | 
			
		||||
) : MessagesFilesCache<K> {
 | 
			
		||||
    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<String>(
 | 
			
		||||
            folderFile
 | 
			
		||||
        ) { it }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user