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 755e430..817cc92 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 @@ -1,6 +1,7 @@ package dev.inmo.tgbotapi.libraries.cache.media.common import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.DeleteMessage import dev.inmo.tgbotapi.requests.DownloadFileStream import dev.inmo.tgbotapi.requests.abstracts.MultipartFile import dev.inmo.tgbotapi.requests.get.GetFile @@ -16,6 +17,9 @@ class DefaultMessageContentCache( private val bot: TelegramBot, private val filesRefreshingChatId: ChatId, private val simpleMessageContentCache: MessagesSimpleCache, + private val mediaFileActualityChecker: MediaFileActualityChecker = MediaFileActualityChecker.WithDelay( + MediaFileActualityChecker.Default(filesRefreshingChatId) + ), private val messagesFilesCache: MessagesFilesCache = InMemoryMessagesFilesCache() ) : MessageContentCache { override suspend fun save(content: MessageContent): K { @@ -48,6 +52,10 @@ class DefaultMessageContentCache( messagesFilesCache.set(key, filename, inputAllocator) }.onFailure { simpleMessageContentCache.remove(key) + }.onSuccess { + with(mediaFileActualityChecker) { + bot.saved(content) + } } return key @@ -56,54 +64,50 @@ class DefaultMessageContentCache( 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(k) ?: error("Unexpected absence of $k file for content ($simpleMessageContentCache)") - val newContent = bot.execute( - when (savedSimpleContent.asInputMedia()) { - is InputMediaAnimation -> SendAnimation( - filesRefreshingChatId, - MultipartFile( - savedFileContentAllocator - ), - disableNotification = true - ) - is InputMediaAudio -> SendAudio( - filesRefreshingChatId, - MultipartFile( - savedFileContentAllocator - ), - disableNotification = true - ) - is InputMediaVideo -> SendVideo( - filesRefreshingChatId, - MultipartFile( - savedFileContentAllocator - ), - disableNotification = true - ) - is InputMediaDocument -> SendDocument( - filesRefreshingChatId, - MultipartFile( - savedFileContentAllocator - ), - disableNotification = true - ) - is InputMediaPhoto -> SendPhoto( - filesRefreshingChatId, - MultipartFile( - savedFileContentAllocator - ), - disableNotification = true - ) - } - ) + if (savedSimpleContent is MediaContent && !with(mediaFileActualityChecker) { bot.isActual(savedSimpleContent) }) { + val savedFileContentAllocator = messagesFilesCache.get(k) ?: error("Unexpected absence of $k file for content ($simpleMessageContentCache)") + val newContent = bot.execute( + when (savedSimpleContent.asInputMedia()) { + is InputMediaAnimation -> SendAnimation( + filesRefreshingChatId, + MultipartFile( + savedFileContentAllocator + ), + disableNotification = true + ) + is InputMediaAudio -> SendAudio( + filesRefreshingChatId, + MultipartFile( + savedFileContentAllocator + ), + disableNotification = true + ) + is InputMediaVideo -> SendVideo( + filesRefreshingChatId, + MultipartFile( + savedFileContentAllocator + ), + disableNotification = true + ) + is InputMediaDocument -> SendDocument( + filesRefreshingChatId, + MultipartFile( + savedFileContentAllocator + ), + disableNotification = true + ) + is InputMediaPhoto -> SendPhoto( + filesRefreshingChatId, + MultipartFile( + savedFileContentAllocator + ), + disableNotification = true + ) + } + ) - simpleMessageContentCache.update(k, newContent.content) - return newContent.content - } + simpleMessageContentCache.update(k, newContent.content) + return newContent.content } return savedSimpleContent } @@ -122,7 +126,10 @@ class DefaultMessageContentCache( bot: TelegramBot, filesRefreshingChatId: ChatId, simpleMessageContentCache: MessagesSimpleCache = InMemoryMessagesSimpleCache(), + mediaFileActualityChecker: MediaFileActualityChecker = MediaFileActualityChecker.WithDelay( + MediaFileActualityChecker.Default(filesRefreshingChatId) + ), messagesFilesCache: MessagesFilesCache = InMemoryMessagesFilesCache() - ) = DefaultMessageContentCache(bot, filesRefreshingChatId, simpleMessageContentCache, messagesFilesCache) + ) = DefaultMessageContentCache(bot, filesRefreshingChatId, simpleMessageContentCache, mediaFileActualityChecker, messagesFilesCache) } } diff --git a/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MediaFileActualityChecker.kt b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MediaFileActualityChecker.kt new file mode 100644 index 0000000..a82127f --- /dev/null +++ b/cache/content/common/src/commonMain/kotlin/dev/inmo/tgbotapi/libraries/cache/media/common/MediaFileActualityChecker.kt @@ -0,0 +1,55 @@ +package dev.inmo.tgbotapi.libraries.cache.media.common + +import com.soywiz.klock.DateTime +import com.soywiz.klock.milliseconds +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.DeleteMessage +import dev.inmo.tgbotapi.requests.abstracts.FileId +import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.MilliSeconds +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent + +fun interface MediaFileActualityChecker { + suspend fun TelegramBot.isActual(mediaContent: MediaContent): Boolean + suspend fun TelegramBot.saved(mediaContent: MediaContent) {} + + class Default( + private val checkingChatId: ChatId + ) : MediaFileActualityChecker { + override suspend fun TelegramBot.isActual(mediaContent: MediaContent): Boolean { + return runCatching { + execute(mediaContent.createResend(checkingChatId)).also { sentMessage -> + execute(DeleteMessage(sentMessage.chat.id, sentMessage.messageId)) + } + }.isSuccess + } + } + + class WithDelay( + private val underhoodChecker: MediaFileActualityChecker, + private val checkingDelay: MilliSeconds = 24 * 60 * 60 * 1000L // one day + ) : MediaFileActualityChecker { + private val fileIdChecksMap = mutableMapOf() + private val checkingDelayTimeSpan = checkingDelay.milliseconds + + override suspend fun TelegramBot.isActual(mediaContent: MediaContent): Boolean { + val now = DateTime.now() + val lastCheck = fileIdChecksMap[mediaContent.media.fileId] + return if (lastCheck == null || now - lastCheck > checkingDelayTimeSpan) { + with(underhoodChecker) { + isActual(mediaContent) + }.also { + if (it) { + fileIdChecksMap[mediaContent.media.fileId] = now + } + } + } else { + true + } + } + + override suspend fun TelegramBot.saved(mediaContent: MediaContent) { + fileIdChecksMap[mediaContent.media.fileId] = DateTime.now() + } + } +} diff --git a/gradle.properties b/gradle.properties index 260a53d..07d01f1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,8 +11,8 @@ kotlin_serialisation_core_version=1.3.2 github_release_plugin_version=2.2.12 -tgbotapi_version=0.38.12 -micro_utils_version=0.9.20 +tgbotapi_version=0.38.13 +micro_utils_version=0.9.22 exposed_version=0.37.3 plagubot_version=0.5.1 @@ -33,5 +33,5 @@ dokka_version=1.6.10 # Project data group=dev.inmo -version=0.0.17 -android_code_version=17 +version=0.0.18 +android_code_version=18