diff --git a/gradle.properties b/gradle.properties index e27f697..d806b29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,10 +13,10 @@ kotlin_serialisation_core_version=1.4.1 github_release_plugin_version=2.4.1 -tgbotapi_version=5.0.0 -micro_utils_version=0.16.4 +tgbotapi_version=5.1.0 +micro_utils_version=0.16.8 exposed_version=0.41.1 -plagubot_version=3.3.0 +plagubot_version=3.4.0 # Dokka @@ -25,4 +25,4 @@ dokka_version=1.7.20 # Project data group=dev.inmo -version=0.7.0 +version=0.8.0 diff --git a/resender/build.gradle b/resender/build.gradle new file mode 100644 index 0000000..9a85f4f --- /dev/null +++ b/resender/build.gradle @@ -0,0 +1,17 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" +} + +apply from: "$mppJavaWithJsProjectPath" + +kotlin { + sourceSets { + commonMain { + dependencies { + api "dev.inmo:tgbotapi.core:$tgbotapi_version" + } + } + } +} + diff --git a/resender/src/commonMain/kotlin/MessageMetaInfo.kt b/resender/src/commonMain/kotlin/MessageMetaInfo.kt new file mode 100644 index 0000000..f844c63 --- /dev/null +++ b/resender/src/commonMain/kotlin/MessageMetaInfo.kt @@ -0,0 +1,18 @@ +package dev.inmo.tgbotapi.libraries.resender + +import dev.inmo.tgbotapi.types.IdChatIdentifier +import dev.inmo.tgbotapi.types.MessageId +import dev.inmo.tgbotapi.types.message.abstracts.Message +import dev.inmo.tgbotapi.types.message.abstracts.PossiblyMediaGroupMessage +import kotlinx.serialization.Serializable + +@Serializable +data class MessageMetaInfo( + val chatId: IdChatIdentifier, + val messageId: MessageId, + val group: String? = null +) + +operator fun MessageMetaInfo.Companion.invoke( + message: Message +) = MessageMetaInfo(message.chat.id, message.messageId, (message as? PossiblyMediaGroupMessage<*>) ?.mediaGroupId) diff --git a/resender/src/commonMain/kotlin/MessagesResender.kt b/resender/src/commonMain/kotlin/MessagesResender.kt new file mode 100644 index 0000000..63d8893 --- /dev/null +++ b/resender/src/commonMain/kotlin/MessagesResender.kt @@ -0,0 +1,144 @@ +package dev.inmo.tgbotapi.libraries.resender + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.ForwardMessage +import dev.inmo.tgbotapi.requests.send.CopyMessage +import dev.inmo.tgbotapi.requests.send.media.SendMediaGroup +import dev.inmo.tgbotapi.types.ChatIdentifier +import dev.inmo.tgbotapi.types.IdChatIdentifier +import dev.inmo.tgbotapi.types.mediaCountInMediaGroup +import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage +import dev.inmo.tgbotapi.types.message.content.MediaGroupPartContent + +class MessagesResender( + private val bot: TelegramBot, + private val cacheChatId: ChatIdentifier +) { + suspend fun resend( + targetChatId: IdChatIdentifier, + messagesInfo: List + ): List> { + val messagesWithOrders = messagesInfo.mapIndexed { i, messageInfo -> messageInfo to i }.toMap() + val ordersWithMessagesGroups = messagesInfo.groupBy { it.group }.flatMap { (group, list) -> + if (group == null) { + list.map { + messagesWithOrders.getValue(it) to listOf(it) + } + } else { + listOf(messagesWithOrders.getValue(list.first()) to list) + } + }.sortedBy { it.first } + + return ordersWithMessagesGroups.flatMap { (_, contents) -> + val sourceMessagesToSentMessages = mutableListOf>() + + when { + contents.size == 1 -> { + val messageInfo = contents.first() + runCatching { + MessageMetaInfo( + targetChatId, + bot.execute( + CopyMessage( + targetChatId, + fromChatId = messageInfo.chatId, + messageId = messageInfo.messageId + ) + ) + ) + }.onFailure { _ -> + runCatching { + bot.execute( + ForwardMessage( + toChatId = targetChatId, + fromChatId = messageInfo.chatId, + messageId = messageInfo.messageId + ) + ) + }.onSuccess { + MessageMetaInfo( + targetChatId, + bot.execute( + CopyMessage( + targetChatId, + fromChatId = it.chat.id, + messageId = it.messageId + ) + ) + ) + } + }.getOrNull() ?.let { + messageInfo to it + } + } + else -> { + val resultContents = contents.mapNotNull { + it to ( + bot.execute( + ForwardMessage( + toChatId = cacheChatId, + fromChatId = it.chatId, + messageId = it.messageId + ) + ) as? ContentMessage<*> ?: return@mapNotNull null) + }.mapNotNull { (src, forwardedMessage) -> + val forwardedMessageAsMediaPartMessage = forwardedMessage.takeIf { + it.content is MediaGroupPartContent + } ?.let { + it as ContentMessage + } + src to (forwardedMessageAsMediaPartMessage ?: null.also { _ -> + sourceMessagesToSentMessages.add( + src to MessageMetaInfo( + targetChatId, + bot.execute( + CopyMessage( + targetChatId, + fromChatId = forwardedMessage.chat.id, + messageId = forwardedMessage.messageId + ) + ) + ) + ) + } ?: return@mapNotNull null) + } + + resultContents.singleOrNull() ?.also { (src, it) -> + sourceMessagesToSentMessages.add( + src to MessageMetaInfo( + targetChatId, + bot.execute( + CopyMessage( + targetChatId, + it.chat.id, + it.messageId + ) + ) + ) + ) + } ?: resultContents.chunked(mediaCountInMediaGroup.last).forEach { + bot.execute( + SendMediaGroup( + targetChatId, + it.map { it.second.content.toMediaGroupMemberTelegramMedia() } + ) + ).content.group.mapIndexed { i, partWrapper -> + it.getOrNull(i) ?.let { + sourceMessagesToSentMessages.add( + it.first to MessageMetaInfo( + partWrapper.sourceMessage.chat.id, + partWrapper.sourceMessage.messageId, + partWrapper.sourceMessage.mediaGroupId + ) + ) + } + } + } + } + } + + sourceMessagesToSentMessages.toList() + } + + } +} diff --git a/settings.gradle b/settings.gradle index ccfc849..feb9455 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,6 +7,8 @@ String[] includes = [ ":cache:content:common", ":cache:content:micro_utils", + + ":resender", ]