
com.github.insanusmokrassar.AutoPostTelegramBot.plugins.publishers.PostPublisher.kt Maven / Gradle / Ivy
package com.github.insanusmokrassar.AutoPostTelegramBot.plugins.publishers
import com.github.insanusmokrassar.AutoPostTelegramBot.base.database.tables.PostsMessagesTable
import com.github.insanusmokrassar.AutoPostTelegramBot.base.database.tables.PostsTable
import com.github.insanusmokrassar.AutoPostTelegramBot.base.models.FinalConfig
import com.github.insanusmokrassar.AutoPostTelegramBot.base.models.PostMessage
import com.github.insanusmokrassar.AutoPostTelegramBot.base.plugins.PluginManager
import com.github.insanusmokrassar.AutoPostTelegramBot.base.plugins.commonLogger
import com.github.insanusmokrassar.AutoPostTelegramBot.plugins.base.commands.deletePost
import com.github.insanusmokrassar.AutoPostTelegramBot.base.plugins.abstractions.Chooser
import com.github.insanusmokrassar.AutoPostTelegramBot.utils.extensions.sendToLogger
import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestsExecutor
import com.github.insanusmokrassar.TelegramBotAPI.requests.DeleteMessage
import com.github.insanusmokrassar.TelegramBotAPI.requests.ForwardMessage
import com.github.insanusmokrassar.TelegramBotAPI.requests.send.SendMessage
import com.github.insanusmokrassar.TelegramBotAPI.requests.send.media.SendMediaGroup
import com.github.insanusmokrassar.TelegramBotAPI.requests.send.media.membersCountInMediaGroup
import com.github.insanusmokrassar.TelegramBotAPI.types.*
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.ContentMessage
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.Message
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.abstracts.MediaGroupContent
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.media.PhotoContent
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.media.VideoContent
import com.github.insanusmokrassar.TelegramBotAPI.utils.extensions.executeUnsafe
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.Channel
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import java.lang.ref.WeakReference
typealias PostIdListPostMessagesTelegramMessages = Pair>
private typealias ChatIdMessageIdPair = Pair
@Serializable
class PostPublisher : Publisher {
@Transient
val postPublishedChannel = BroadcastChannel(
Channel.CONFLATED
)
@Transient
private var botWR: WeakReference? = null
@Transient
private var sourceChatId: ChatId? = null
@Transient
private var targetChatId: ChatId? = null
@Transient
private var logsChatId: ChatId? = null
@Transient
private var publishPostCommand: PublishPost? = null
override suspend fun onInit(
executor: RequestsExecutor,
baseConfig: FinalConfig,
pluginManager: PluginManager
) {
botWR = WeakReference(executor).also {
publishPostCommand = PublishPost(
pluginManager.plugins.firstOrNull { it is Chooser } as? Chooser,
pluginManager.plugins.firstOrNull { it is Publisher } as Publisher,
it,
baseConfig.logsChatId
)
}
sourceChatId = baseConfig.sourceChatId
targetChatId = baseConfig.targetChatId
logsChatId = baseConfig.logsChatId
}
override suspend fun publishPost(postId: Int) {
val executor = botWR ?.get() ?: return
val sourceChatId: ChatId = sourceChatId ?: return
val targetChatId: ChatId = targetChatId ?: return
val logsChatId: ChatId = logsChatId ?: return
val messagesToDelete = mutableListOf()
try {
executor.execute(
SendMessage(
logsChatId,
"Start post"
)
).asMessage.let {
messagesToDelete.add(it.chat.id to it.messageId)
}
val messagesOfPost = mutableListOf()
PostsMessagesTable.getMessagesOfPost(postId).also {
it.forEach { message ->
executor.executeUnsafe(
ForwardMessage(
sourceChatId,
logsChatId,
message.messageId,
disableNotification = true
),
retries = 3
) ?.asMessage ?.also {
messagesToDelete.add(it.chat.id to it.messageId)
message.message = it
messagesOfPost.add(message)
} ?: message.messageId.let {
commonLogger.warning(
"Can't forward message with id: $it; it will be removed from post"
)
PostsMessagesTable.removePostMessage(postId, it)
}
}
}
if (messagesOfPost.isEmpty()) {
PostsTable.removePost(postId)
commonLogger.warning("Post $postId will be removed cause it contains not publishable messages")
return
}
val responses = mutableListOf>()
var mediaGroup: MutableList? = null
try {
messagesOfPost.forEach { postMessage ->
//TODO:: REFACTOR
mediaGroup?.let { currentMediaGroup ->
if (postMessage.mediaGroupId != currentMediaGroup.first().mediaGroupId) {
mediaGroup = null
responses += sendMediaGroup(executor, targetChatId, currentMediaGroup)
null
} else {
currentMediaGroup.add(postMessage)
}
} ?: also {
if (postMessage.mediaGroupId != null) {
mediaGroup = mutableListOf().apply {
add(postMessage)
}
} else {
(postMessage.message as? ContentMessage<*>)?.content?.createResends(
targetChatId
)?.forEach { request ->
responses.add(postMessage to executor.execute(request).asMessage)
}
}
}
}
} catch (e: Exception) {
responses.forEach { (_, response) ->
executor.executeUnsafe(
DeleteMessage(
response.chat.id,
response.messageId
)
)
}
throw e
}
mediaGroup ?.also {
responses += sendMediaGroup(executor, targetChatId, it)
}
responses.also {
it.forEach { (_, message) ->
try {
executor.execute(
ForwardMessage(
message.chat.id,
logsChatId,
message.messageId
)
)
} catch (e: Exception) {
commonLogger.warning(
"Can't forward message with id: ${message.messageId}"
)
}
}
postPublishedChannel.send(
PostIdListPostMessagesTelegramMessages(
postId,
it.toMap()
)
)
}
deletePost(
executor,
sourceChatId,
postId
)
} catch (e: Throwable) {
sendToLogger(e, "Publish post")
} finally {
messagesToDelete.forEach {
executor.executeUnsafe(
DeleteMessage(
it.first,
it.second
)
)
}
}
}
private suspend fun sendMediaGroup(
executor: RequestsExecutor,
targetChatId: ChatIdentifier,
mediaGroup: List
): List> {
return when {
mediaGroup.size < 2 -> {
val postMessage = mediaGroup.firstOrNull() ?: return emptyList()
val contentMessage = (postMessage.message as? ContentMessage<*>) ?: return emptyList()
val request = contentMessage.content.createResend(
targetChatId
)
val response = executor.execute(request)
listOf(
postMessage to response.asMessage
)
}
mediaGroup.size in membersCountInMediaGroup -> {
val mediaGroupContent = mediaGroup.mapNotNull {
((it.message as? ContentMessage<*>) ?.content as? MediaGroupContent) ?.toMediaGroupMemberInputMedia() ?.let { media ->
it to media
}
}.toMap()
val request = SendMediaGroup(
targetChatId,
mediaGroupContent.values.toList()
)
val response = executor.execute(request)
val contentResponse = response.mapNotNull { it.asMessage as? ContentMessage<*> }
contentResponse.mapNotNull {
val content = it.content
when (content) {
is PhotoContent -> mediaGroupContent.keys.firstOrNull { postMessage ->
mediaGroupContent[postMessage] ?.file == content.media.fileId
}
is VideoContent -> mediaGroupContent.keys.firstOrNull { postMessage ->
mediaGroupContent[postMessage] ?.file == content.media.fileId
}
else -> null
} ?.let { postMessage ->
postMessage to it
}
}
}
else -> mediaGroup.chunked(membersCountInMediaGroup.endInclusive).flatMap { postMessages ->
sendMediaGroup(executor, targetChatId, postMessages)
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy