1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2026-08-20 08:06:21 +00:00

improve richblocks api and let rich message content to be correctly resendable if there are no any media block inside

This commit is contained in:
2026-07-01 14:25:39 +06:00
parent 5341b2eccf
commit 68987feabb
6 changed files with 139 additions and 34 deletions

View File

@@ -1,6 +1,8 @@
package dev.inmo.tgbotapi.types.message.content
import dev.inmo.tgbotapi.requests.ForwardMessage
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.CopyMessage
import dev.inmo.tgbotapi.requests.send.SendRichMessage
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
@@ -9,8 +11,14 @@ import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.message.SuggestedPostParameters
import dev.inmo.tgbotapi.types.message.abstracts.ChatContentMessage
import dev.inmo.tgbotapi.types.rich.InputRichMessageMarkdown
import dev.inmo.tgbotapi.types.rich.RichBlock
import dev.inmo.tgbotapi.types.rich.RichBlockAudio
import dev.inmo.tgbotapi.types.rich.RichBlockMedia
import dev.inmo.tgbotapi.types.rich.RichBlockPhoto
import dev.inmo.tgbotapi.types.rich.RichBlockVideo
import dev.inmo.tgbotapi.types.rich.RichTextInfo
import dev.inmo.tgbotapi.types.rich.markdown
import dev.inmo.tgbotapi.types.rich.search
import kotlinx.serialization.Serializable
@Serializable
@@ -32,19 +40,39 @@ data class RichMessageContent(
replyParameters: ReplyParameters?,
replyMarkup: KeyboardMarkup?
): Request<ChatContentMessage<RichMessageContent>> {
return SendRichMessage(
chatId = chatId,
richMessage = InputRichMessageMarkdown(richMessage.markdown, isRtl = richMessage.isRtl),
threadId = messageThreadId,
directMessageThreadId = directMessageThreadId,
businessConnectionId = businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
allowPaidBroadcast = allowPaidBroadcast,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)
val isThereMedia = richMessage.blocks.any {
it.search {
this is RichBlockMedia
} != null
}
return if (isThereMedia) {
@Suppress("UNCHECKED_CAST")
ForwardMessage(
chat.id,
toChatId = chatId,
messageId = messageId,
threadId = messageThreadId,
directMessageThreadId = directMessageThreadId,
disableNotification = disableNotification,
protectContent = protectContent,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
) as Request<ChatContentMessage<RichMessageContent>>
} else {
SendRichMessage(
chatId = chatId,
richMessage = InputRichMessageMarkdown(richMessage.markdown, isRtl = richMessage.isRtl),
threadId = messageThreadId,
directMessageThreadId = directMessageThreadId,
businessConnectionId = businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
allowPaidBroadcast = allowPaidBroadcast,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)
}
}
}

View File

@@ -1,5 +1,7 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.files.TelegramMediaFile
import dev.inmo.tgbotapi.types.typeField
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import kotlinx.serialization.DeserializationStrategy
@@ -30,6 +32,39 @@ sealed interface RichBlock {
val html: String
}
@Serializable(RichBlockSerializer::class)
sealed interface RichBlockMedia : RichBlock {
val media: TelegramMediaFile
val caption: RichBlockCaption?
}
/**
* The nested [RichBlock]s directly contained by this block, or an empty list for leaf blocks. Container blocks
* ([RichBlockList] via its [RichBlockListItem.blocks], [RichBlockBlockQuotation], [RichBlockCollage],
* [RichBlockSlideshow] and [RichBlockDetails]) expose their children here.
*/
val RichBlock.subBlocks: List<RichBlock>
get() = when (this) {
is RichBlockList -> items.flatMap { it.blocks }
is RichBlockBlockQuotation -> blocks
is RichBlockCollage -> blocks
is RichBlockSlideshow -> blocks
is RichBlockDetails -> blocks
else -> emptyList()
}
/**
* Walks this [RichBlock] and all of its [subBlocks] recursively (depth-first, this block first) and returns the first
* block for which [block] returns `true`, or `null` if none matches.
*/
fun RichBlock.search(block: RichBlock.() -> Boolean): RichBlock? {
if (block()) return this
for (child in subBlocks) {
child.search(block)?.let { return it }
}
return null
}
object RichBlockSerializer : JsonContentPolymorphicSerializer<RichBlock>(RichBlock::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<RichBlock> {
return when (val type = element.jsonObject[typeField]?.jsonPrimitive?.content) {

View File

@@ -10,6 +10,7 @@ import dev.inmo.tgbotapi.types.expressionField
import dev.inmo.tgbotapi.types.files.AnimationFile
import dev.inmo.tgbotapi.types.files.AudioFile
import dev.inmo.tgbotapi.types.files.PhotoFile
import dev.inmo.tgbotapi.types.files.TelegramMediaFile
import dev.inmo.tgbotapi.types.files.VideoFile
import dev.inmo.tgbotapi.types.files.VoiceFile
import dev.inmo.tgbotapi.types.hasSpoilerField
@@ -537,8 +538,8 @@ data class RichBlockAnimation(
@SerialName(hasSpoilerField)
val hasSpoiler: Boolean? = null,
@SerialName(captionField)
val caption: RichBlockCaption? = null
) : RichBlock {
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
@@ -546,6 +547,9 @@ data class RichBlockAnimation(
override val markdown: String = markdown(animation, caption)
override val html: String = html(animation, hasSpoiler, caption)
override val media: TelegramMediaFile
get() = animation
companion object {
const val TYPE = "animation"
fun markdown(animation: AnimationFile, caption: RichBlockCaption?): String =
@@ -565,14 +569,16 @@ data class RichBlockAudio(
@SerialName(audioField)
val audio: AudioFile,
@SerialName(captionField)
val caption: RichBlockCaption? = null
) : RichBlock {
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(audio, caption)
override val html: String = html(audio, caption)
override val media: TelegramMediaFile
get() = audio
companion object {
const val TYPE = "audio"
@@ -595,14 +601,16 @@ data class RichBlockPhoto(
@SerialName(hasSpoilerField)
val hasSpoiler: Boolean? = null,
@SerialName(captionField)
val caption: RichBlockCaption? = null
) : RichBlock {
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(photo, caption)
override val html: String = html(photo, hasSpoiler, caption)
override val media: TelegramMediaFile
get() = photo
companion object {
const val TYPE = "photo"
@@ -625,14 +633,16 @@ data class RichBlockVideo(
@SerialName(hasSpoilerField)
val hasSpoiler: Boolean? = null,
@SerialName(captionField)
val caption: RichBlockCaption? = null
) : RichBlock {
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(video, caption)
override val html: String = html(video, hasSpoiler, caption)
override val media: TelegramMediaFile
get() = video
companion object {
const val TYPE = "video"
@@ -653,14 +663,16 @@ data class RichBlockVoiceNote(
@SerialName(voiceNoteField)
val voiceNote: VoiceFile,
@SerialName(captionField)
val caption: RichBlockCaption? = null
) : RichBlock {
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(voiceNote, caption)
override val html: String = html(voiceNote, caption)
override val media: TelegramMediaFile
get() = voiceNote
companion object {
const val TYPE = "voice_note"