diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c3b61ffa9..13a35bcad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,61 @@ # TelegramBotAPI changelog +## 0.29.3 + +* `Common`: + * Version updates: + * `Serialization`: `1.0.0` -> `1.0.1` +* `Core`: + * New annotation `RiskFeature`. This annotation will be applied to the things which contains unsafe types usage + * `SendMediaGroup` factory now marked with `RiskFeature` + * Media groups updates: + * New functions `SendPlaylist` + * New functions `SendDocumentsGroup` + * New functions `SendVisualMediaGroup` + * New type `VisualMediaGroupMemberInputMedia : MediaGroupMemberInputMedia` + * `InputMediaPhoto` now implements `VisualMediaGroupMemberInputMedia` instead of `MediaGroupMemberInputMedia` + * `InputMediaVideo` now implements `VisualMediaGroupMemberInputMedia` instead of `MediaGroupMemberInputMedia` + * New type `VisualMediaGroupContent : MediaGroupContent` + * `PhotoContent` now implements `VisualMediaGroupContent` instead of `MediaGroupContent` + * `VideoContent` now implements `VisualMediaGroupContent` instead of `MediaGroupContent` + * New type `AudioMediaGroupContent : MediaGroupContent` + * `AudioContent` now implements `AudioMediaGroupContent` instead of `MediaContent` and `CaptionedInput` + * New type `DocumentMediaGroupContent : MediaGroupContent` + * `DocumentContent` now implements `DocumentMediaGroupContent` instead of `MediaContent` and `CaptionedInput` + * New type `AudioMediaGroupMemberInputMedia : MediaGroupMemberInputMedia` + * `InputMediaAudio` now implements `AudioMediaGroupMemberInputMedia` + * New type `DocumentMediaGroupMemberInputMedia : MediaGroupMemberInputMedia` + * `InputMediaDocument` now implements `DocumentMediaGroupMemberInputMedia` + * New extension `AudioFile#toInputMediaAudio` + * `AudioContent` now implements `MediaGroupContent` + * New extension `DocumentFile#toInputMediaDocument` + * `DocumentContent` now implements `MediaGroupContent` + * New dice type `SlotMachineDiceAnimationType` + * New extension `TelegramMediaFile#asDocumentFile` + * New extension `VideoFile#toInputMediaVideo` + * New exception `WrongFileIdentifierException` + * Extension `String#toInputMediaFileAttachmentName` now is deprecated + * Property `ThumbedInputMedia#thumbMedia` now is deprecated +* `API`: + * New extensions for media groups: + * `TelegramBot#sendPlaylist` + * `TelegramBot#replyWithPlaylist` + * `TelegramBot#sendDocumentsGroup` + * `TelegramBot#replyWithDocumentsGroup` + * `TelegramBot#sendVisualMediaGroup` + * `TelegramBot#replyWithVisualMediaGroup` +* `Utils`: + * New extensions for `Flow`s: + * `Flow#mediaGroupVisualMessages` + * `Flow#mediaGroupAudioMessages` + * `Flow#mediaGroupDocumentMessages` + * New extensions for `FlowsUpdatesFilter`: + * `FlowsUpdatesFilter#audioMessagesWithMediaGroups` + * `FlowsUpdatesFilter#mediaGroupAudioMessages` + * `FlowsUpdatesFilter#documentMessagesWithMediaGroups` + * `FlowsUpdatesFilter#mediaGroupDocumentMessages` + * `FlowsUpdatesFilter#mediaGroupVisualMessages` + ## 0.29.2 * `Common`: diff --git a/gradle.properties b/gradle.properties index fab9a91783..4cbff7cb7d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ kotlin.incremental.js=true kotlin_version=1.4.10 kotlin_coroutines_version=1.4.0 -kotlin_serialisation_runtime_version=1.0.0 +kotlin_serialisation_runtime_version=1.0.1 klock_version=1.12.1 uuid_version=0.2.2 ktor_version=1.4.1 @@ -15,7 +15,7 @@ ktor_version=1.4.1 javax_activation_version=1.1.1 library_group=dev.inmo -library_version=0.29.2 +library_version=0.29.3 gradle_bintray_plugin_version=1.8.5 github_release_plugin_version=2.2.12 diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/exceptions/RequestException.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/exceptions/RequestException.kt index de4d1d0480..b354610645 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/exceptions/RequestException.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/exceptions/RequestException.kt @@ -15,6 +15,7 @@ fun newRequestException( description.contains("Bad Request: message is not modified") -> MessageIsNotModifiedException(response, plainAnswer, message, cause) description == "Unauthorized" -> UnauthorizedException(response, plainAnswer, message, cause) description.contains("PHOTO_INVALID_DIMENSIONS") -> InvalidPhotoDimensionsException(response, plainAnswer, message, cause) + description.contains("wrong file identifier") -> WrongFileIdentifierException(response, plainAnswer, message, cause) else -> null } } ?: CommonRequestException(response, plainAnswer, message, cause) @@ -45,3 +46,6 @@ class MessageToEditNotFoundException(response: Response, plainAnswer: String, me class InvalidPhotoDimensionsException(response: Response, plainAnswer: String, message: String?, cause: Throwable?) : RequestException(response, plainAnswer, message, cause) + +class WrongFileIdentifierException(response: Response, plainAnswer: String, message: String?, cause: Throwable?) : + RequestException(response, plainAnswer, message, cause) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt index 653d09f957..29ba40120d 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt @@ -1,6 +1,5 @@ package dev.inmo.tgbotapi.requests.abstracts -import dev.inmo.tgbotapi.types.InputMedia.toInputMediaFileAttachmentName import dev.inmo.tgbotapi.utils.StorageFile import kotlinx.serialization.* import kotlinx.serialization.descriptors.* @@ -12,6 +11,14 @@ sealed class InputFile { abstract val fileId: String } +internal inline val InputFile.attachFileId + get() = "attach://$fileId" +internal inline val InputFile.fileIdToSend + get() = when (this) { + is FileId -> fileId + is MultipartFile -> attachFileId + } + // TODO:: add checks for file url/file id regex /** * Contains file id or file url @@ -30,12 +37,6 @@ internal object InputFileSerializer : KSerializer { override fun deserialize(decoder: Decoder): FileId = FileId(decoder.decodeString()) } -internal val InputFile.asMediaData: String - get() = when (this) { - is FileId -> fileId - is MultipartFile -> fileId.toInputMediaFileAttachmentName() - } - // TODO:: add checks for files size /** * Contains info about file for sending diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/send/media/SendMediaGroup.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/send/media/SendMediaGroup.kt index fb458dbd9a..c49a398ec4 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/send/media/SendMediaGroup.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/send/media/SendMediaGroup.kt @@ -8,12 +8,16 @@ import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.InputMedia.* import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializerClass +import dev.inmo.tgbotapi.utils.* import dev.inmo.tgbotapi.utils.throwRangeError -import dev.inmo.tgbotapi.utils.toJsonWithoutNulls import kotlinx.serialization.* import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.json.buildJsonArray +const val rawSendingMediaGroupsWarning = "Media groups contains restrictions related to combinations of media" + + " types. Currently it is possible to combine photo + video OR audio OR documents" + +@RiskFeature(rawSendingMediaGroupsWarning) fun SendMediaGroup( chatId: ChatIdentifier, media: List, @@ -52,6 +56,46 @@ fun SendMediaGroup( } } +/** + * Use this method to be sure that you are correctly sending playlist with audios + * + * @see InputMediaAudio + */ +@Suppress("NOTHING_TO_INLINE") +inline fun SendPlaylist( + chatId: ChatIdentifier, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = SendMediaGroup(chatId, media, disableNotification, replyToMessageId) + +/** + * Use this method to be sure that you are correctly sending documents media group + * + * @see InputMediaDocument + */ +@Suppress("NOTHING_TO_INLINE") +inline fun SendDocumentsGroup( + chatId: ChatIdentifier, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = SendMediaGroup(chatId, media, disableNotification, replyToMessageId) + +/** + * Use this method to be sure that you are correctly sending visual media group + * + * @see InputMediaPhoto + * @see InputMediaVideo + */ +@Suppress("NOTHING_TO_INLINE") +inline fun SendVisualMediaGroup( + chatId: ChatIdentifier, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = SendMediaGroup(chatId, media, disableNotification, replyToMessageId) + private val messagesListSerializer: KSerializer> = ListSerializer(TelegramBotAPIMessageDeserializeOnlySerializerClass()) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMedia.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMedia.kt index 7942ed7ccf..48e99180ca 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMedia.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMedia.kt @@ -3,6 +3,7 @@ package dev.inmo.tgbotapi.types.InputMedia import dev.inmo.tgbotapi.requests.abstracts.InputFile import kotlinx.serialization.Serializable +@Deprecated("Will be removed due to redundancy for end-side users") fun String.toInputMediaFileAttachmentName() = "attach://$this" @Serializable(InputMediaSerializer::class) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaAnimation.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaAnimation.kt index 6d96174261..c56a9044f3 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaAnimation.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaAnimation.kt @@ -23,5 +23,5 @@ data class InputMediaAnimation( @SerialName(mediaField) override val media: String - init { media = file.fileId } // crutch until js compiling will be fixed + init { media = file.fileIdToSend } // crutch until js compiling will be fixed } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaAudio.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaAudio.kt index 461d4f217b..1c3b547428 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaAudio.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaAudio.kt @@ -5,9 +5,13 @@ import dev.inmo.tgbotapi.CommonAbstracts.Performerable import dev.inmo.tgbotapi.requests.abstracts.* import dev.inmo.tgbotapi.types.ParseMode.ParseMode import dev.inmo.tgbotapi.types.ParseMode.parseModeField +import dev.inmo.tgbotapi.types.files.AudioFile +import dev.inmo.tgbotapi.types.files.PhotoSize import dev.inmo.tgbotapi.types.mediaField -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable +import dev.inmo.tgbotapi.types.message.content.media.AudioContent +import kotlinx.serialization.* + +internal const val audioInputMediaType = "audio" @Serializable data class InputMediaAudio( @@ -19,10 +23,26 @@ data class InputMediaAudio( override val performer: String? = null, override val title: String? = null, override val thumb: InputFile? = null -) : InputMedia, DuratedInputMedia, ThumbedInputMedia, TitledInputMedia, CaptionedOutput, Performerable { - override val type: String = "audio" +) : InputMedia, AudioMediaGroupMemberInputMedia, DuratedInputMedia, ThumbedInputMedia, TitledInputMedia, CaptionedOutput, Performerable { + override val type: String = audioInputMediaType + + override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) @SerialName(mediaField) override val media: String - init { media = file.fileId } // crutch until js compiling will be fixed + init { media = file.fileIdToSend } // crutch until js compiling will be fixed } + +fun AudioFile.toInputMediaAudio( + caption: String? = null, + parseMode: ParseMode? = null, + title: String? = this.title +): InputMediaAudio = InputMediaAudio( + fileId, + caption, + parseMode, + duration, + performer, + title, + thumb ?.fileId +) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaDocument.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaDocument.kt index 9d40fa4660..465b630c6a 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaDocument.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaDocument.kt @@ -4,9 +4,11 @@ import dev.inmo.tgbotapi.CommonAbstracts.CaptionedOutput import dev.inmo.tgbotapi.requests.abstracts.* import dev.inmo.tgbotapi.types.ParseMode.ParseMode import dev.inmo.tgbotapi.types.ParseMode.parseModeField +import dev.inmo.tgbotapi.types.files.DocumentFile import dev.inmo.tgbotapi.types.mediaField -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable +import kotlinx.serialization.* + +internal const val documentInputMediaType = "document" @Serializable data class InputMediaDocument( @@ -15,10 +17,22 @@ data class InputMediaDocument( @SerialName(parseModeField) override val parseMode: ParseMode? = null, override val thumb: InputFile? = null -) : InputMedia, ThumbedInputMedia, CaptionedOutput { - override val type: String = "document" +) : InputMedia, DocumentMediaGroupMemberInputMedia, ThumbedInputMedia, CaptionedOutput { + override val type: String = documentInputMediaType + + override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) @SerialName(mediaField) override val media: String - init { media = file.fileId } // crutch until js compiling will be fixed + init { media = file.fileIdToSend } // crutch until js compiling will be fixed } + +fun DocumentFile.toInputMediaDocument( + caption: String? = null, + parseMode: ParseMode? = null +) = InputMediaDocument( + fileId, + caption, + parseMode, + thumb ?.fileId +) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaPhoto.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaPhoto.kt index 432fd8a578..32a5c1b482 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaPhoto.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaPhoto.kt @@ -17,14 +17,14 @@ data class InputMediaPhoto( override val caption: String? = null, @SerialName(parseModeField) override val parseMode: ParseMode? = null -) : InputMedia, MediaGroupMemberInputMedia { +) : InputMedia, VisualMediaGroupMemberInputMedia { override val type: String = photoInputMediaType override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) @SerialName(mediaField) override val media: String - init { media = file.fileId } // crutch until js compiling will be fixed + init { media = file.fileIdToSend } // crutch until js compiling will be fixed } fun PhotoSize.toInputMediaPhoto( diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaVideo.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaVideo.kt index 8be815907a..302a1579d3 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaVideo.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/InputMediaVideo.kt @@ -19,12 +19,12 @@ data class InputMediaVideo( override val height: Int? = null, override val duration: Long? = null, override val thumb: InputFile? = null -) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, MediaGroupMemberInputMedia { +) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, VisualMediaGroupMemberInputMedia { override val type: String = videoInputMediaType override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) @SerialName(mediaField) override val media: String - init { media = file.fileId } // crutch until js compiling will be fixed + init { media = file.fileIdToSend } // crutch until js compiling will be fixed } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/MediaGroupMemberInputMedia.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/MediaGroupMemberInputMedia.kt index a587f121d5..83965179f8 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/MediaGroupMemberInputMedia.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/MediaGroupMemberInputMedia.kt @@ -18,3 +18,9 @@ internal fun T.buildArguments(withSerializer: SerializationStrategy) = ar interface MediaGroupMemberInputMedia : InputMedia, CaptionedOutput { fun serialize(format: StringFormat): String } + +interface AudioMediaGroupMemberInputMedia: MediaGroupMemberInputMedia +interface DocumentMediaGroupMemberInputMedia: MediaGroupMemberInputMedia + +@Serializable(MediaGroupMemberInputMediaSerializer::class) +interface VisualMediaGroupMemberInputMedia : MediaGroupMemberInputMedia diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/MediaGroupMemberInputMediaSerializer.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/MediaGroupMemberInputMediaSerializer.kt index 12faeb251c..2933aa558b 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/MediaGroupMemberInputMediaSerializer.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/MediaGroupMemberInputMediaSerializer.kt @@ -16,6 +16,8 @@ internal object MediaGroupMemberInputMediaSerializer : KSerializer InputMediaPhoto.serializer().serialize(encoder, value) is InputMediaVideo -> InputMediaVideo.serializer().serialize(encoder, value) + is InputMediaAudio -> InputMediaAudio.serializer().serialize(encoder, value) + is InputMediaDocument -> InputMediaDocument.serializer().serialize(encoder, value) } } @@ -25,6 +27,8 @@ internal object MediaGroupMemberInputMediaSerializer : KSerializer nonstrictJsonFormat.decodeFromJsonElement(InputMediaPhoto.serializer(), json) videoInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaVideo.serializer(), json) + audioInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaAudio.serializer(), json) + documentInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaDocument.serializer(), json) else -> error("Illegal type of incoming MediaGroupMemberInputMedia") } } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/ThumbedInputMedia.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/ThumbedInputMedia.kt index 5588de4677..1bda788bf4 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/ThumbedInputMedia.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/InputMedia/ThumbedInputMedia.kt @@ -9,6 +9,7 @@ interface ThumbedInputMedia : InputMedia { val thumb: InputFile? @Serializable @SerialName(thumbField) + @Deprecated("Will be removed due to useless state") val thumbMedia: String? get() = thumb ?.let { when (it) { diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/dice/DiceAnimationType.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/dice/DiceAnimationType.kt index 806e43d16f..74ded7764f 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/dice/DiceAnimationType.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/dice/DiceAnimationType.kt @@ -22,6 +22,10 @@ object BasketballDiceAnimationType : DiceAnimationType() { override val emoji: String = "\uD83C\uDFC0" } @Serializable(DiceAnimationTypeSerializer::class) +object SlotMachineDiceAnimationType : DiceAnimationType() { + override val emoji: String = "\uD83C\uDFB0" +} +@Serializable(DiceAnimationTypeSerializer::class) data class CustomDiceAnimationType( override val emoji: String ) : DiceAnimationType() @@ -34,6 +38,7 @@ internal object DiceAnimationTypeSerializer : KSerializer { CubeDiceAnimationType.emoji -> CubeDiceAnimationType DartsDiceAnimationType.emoji -> DartsDiceAnimationType BasketballDiceAnimationType.emoji -> BasketballDiceAnimationType + SlotMachineDiceAnimationType.emoji -> SlotMachineDiceAnimationType else -> CustomDiceAnimationType(type) } } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/DocumentFile.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/DocumentFile.kt index 41c0ac7854..103fbb78d5 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/DocumentFile.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/DocumentFile.kt @@ -22,3 +22,13 @@ data class DocumentFile( @SerialName(fileNameField) override val fileName: String? = null ) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, CustomNamedMediaFile + +@Suppress("NOTHING_TO_INLINE") +inline fun TelegramMediaFile.asDocumentFile() = DocumentFile( + fileId, + fileUniqueId, + fileSize, + (this as? ThumbedMediaFile) ?.thumb, + (this as? MimedMediaFile) ?.mimeType, + (this as? CustomNamedMediaFile) ?.fileName +) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/VideoFile.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/VideoFile.kt index 84555e0a76..5780caa07c 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/VideoFile.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/VideoFile.kt @@ -2,9 +2,13 @@ package dev.inmo.tgbotapi.types.files import dev.inmo.tgbotapi.requests.abstracts.FileId import dev.inmo.tgbotapi.types.FileUniqueId +import dev.inmo.tgbotapi.types.InputMedia.InputMediaVideo +import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode +import dev.inmo.tgbotapi.types.ParseMode.ParseMode import dev.inmo.tgbotapi.types.fileUniqueIdField import dev.inmo.tgbotapi.types.files.abstracts.* import dev.inmo.tgbotapi.utils.MimeType +import dev.inmo.tgbotapi.utils.toHtmlCaptions import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -23,3 +27,17 @@ data class VideoFile( @SerialName(fileSizeField) override val fileSize: Long? = null ) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, SizedMediaFile + +@Suppress("NOTHING_TO_INLINE") +inline fun VideoFile.toInputMediaVideo( + caption: String? = null, + parseMode: ParseMode? = null +) = InputMediaVideo( + fileId, + caption, + parseMode, + width, + height, + duration, + thumb ?.fileId +) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/RawMessage.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/RawMessage.kt index 4ac90d2823..5f6991b77a 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/RawMessage.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/RawMessage.kt @@ -220,17 +220,20 @@ internal data class RawMessage( } } ?: content?.let { content -> media_group_id?.let { + val checkedContent = when (content) { + is PhotoContent -> content + is VideoContent -> content + is AudioContent -> content + is DocumentContent -> content + else -> error("Unsupported content for media group") + } when (from) { null -> ChannelMediaGroupMessage( messageId, chat, date.asDate, it, - when (content) { - is PhotoContent -> content - is VideoContent -> content - else -> error("Unsupported content for media group") - }, + checkedContent, edit_date?.asDate, forwarded, reply_to_message?.asMessage, @@ -242,11 +245,7 @@ internal data class RawMessage( chat, date.asDate, it, - when (content) { - is PhotoContent -> content - is VideoContent -> content - else -> error("Unsupported content for media group") - }, + checkedContent, edit_date?.asDate, forwarded, reply_to_message?.asMessage, diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/abstracts/MediaGroupContent.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/abstracts/MediaGroupContent.kt index 18cc8686cd..a284c0c4b8 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/abstracts/MediaGroupContent.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/abstracts/MediaGroupContent.kt @@ -5,4 +5,8 @@ import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia interface MediaGroupContent : MediaContent, CaptionedInput { fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia -} \ No newline at end of file +} + +interface VisualMediaGroupContent : MediaGroupContent +interface AudioMediaGroupContent : MediaGroupContent +interface DocumentMediaGroupContent : MediaGroupContent diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/AudioContent.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/AudioContent.kt index dde0d121f2..e662354d3a 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/AudioContent.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/AudioContent.kt @@ -1,18 +1,17 @@ package dev.inmo.tgbotapi.types.message.content.media -import dev.inmo.tgbotapi.CommonAbstracts.CaptionedInput import dev.inmo.tgbotapi.CommonAbstracts.TextPart import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.send.media.SendAudio import dev.inmo.tgbotapi.types.ChatIdentifier -import dev.inmo.tgbotapi.types.InputMedia.InputMediaAudio +import dev.inmo.tgbotapi.types.InputMedia.* import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2 import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.files.AudioFile import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage -import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent +import dev.inmo.tgbotapi.types.message.content.abstracts.* import dev.inmo.tgbotapi.utils.toHtmlCaptions import dev.inmo.tgbotapi.utils.toMarkdownV2Captions @@ -20,7 +19,7 @@ data class AudioContent( override val media: AudioFile, override val caption: String? = null, override val captionEntities: List = emptyList() -) : MediaContent, CaptionedInput { +) : AudioMediaGroupContent { override fun createResend( chatId: ChatIdentifier, disableNotification: Boolean, @@ -40,13 +39,10 @@ data class AudioContent( replyMarkup ) - override fun asInputMedia(): InputMediaAudio = InputMediaAudio( - media.fileId, - toMarkdownV2Captions().firstOrNull(), - MarkdownV2, - media.duration, - media.performer, - media.title, - media.thumb ?.fileId + override fun toMediaGroupMemberInputMedia(): InputMediaAudio = asInputMedia() + + override fun asInputMedia(): InputMediaAudio = media.toInputMediaAudio( + toHtmlCaptions().firstOrNull(), + HTMLParseMode ) } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/DocumentContent.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/DocumentContent.kt index 660347256a..f69c415813 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/DocumentContent.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/DocumentContent.kt @@ -5,14 +5,15 @@ import dev.inmo.tgbotapi.CommonAbstracts.TextPart import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.send.media.SendDocument import dev.inmo.tgbotapi.types.ChatIdentifier -import dev.inmo.tgbotapi.types.InputMedia.InputMediaDocument +import dev.inmo.tgbotapi.types.InputMedia.* import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2 import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.files.DocumentFile +import dev.inmo.tgbotapi.types.files.asDocumentFile import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage -import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent +import dev.inmo.tgbotapi.types.message.content.abstracts.* import dev.inmo.tgbotapi.utils.toHtmlCaptions import dev.inmo.tgbotapi.utils.toMarkdownV2Captions @@ -20,7 +21,7 @@ data class DocumentContent( override val media: DocumentFile, override val caption: String? = null, override val captionEntities: List = emptyList() -) : MediaContent, CaptionedInput { +) : DocumentMediaGroupContent { override fun createResend( chatId: ChatIdentifier, disableNotification: Boolean, @@ -37,10 +38,22 @@ data class DocumentContent( replyMarkup ) - override fun asInputMedia(): InputMediaDocument = InputMediaDocument( - media.fileId, - toMarkdownV2Captions().firstOrNull(), - MarkdownV2, - media.thumb ?.fileId + override fun toMediaGroupMemberInputMedia(): InputMediaDocument = asInputMedia() + + override fun asInputMedia(): InputMediaDocument = media.toInputMediaDocument( + toHtmlCaptions().firstOrNull(), + HTMLParseMode + ) +} + +@Suppress("NOTHING_TO_INLINE") +inline fun MediaContent.asDocumentContent() = when (this) { + is CaptionedInput -> DocumentContent( + media.asDocumentFile(), + caption, + captionEntities + ) + else -> DocumentContent( + media.asDocumentFile() ) } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/PhotoContent.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/PhotoContent.kt index e691370ab3..de7b9eb17a 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/PhotoContent.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/PhotoContent.kt @@ -4,16 +4,14 @@ import dev.inmo.tgbotapi.CommonAbstracts.TextPart import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.send.media.SendPhoto import dev.inmo.tgbotapi.types.ChatIdentifier -import dev.inmo.tgbotapi.types.InputMedia.InputMediaPhoto -import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia +import dev.inmo.tgbotapi.types.InputMedia.* import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2 import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.files.* import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage -import dev.inmo.tgbotapi.types.message.content.abstracts.MediaCollectionContent -import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent +import dev.inmo.tgbotapi.types.message.content.abstracts.* import dev.inmo.tgbotapi.utils.toHtmlCaptions import dev.inmo.tgbotapi.utils.toMarkdownV2Captions @@ -21,7 +19,7 @@ data class PhotoContent( override val mediaCollection: Photo, override val caption: String? = null, override val captionEntities: List = emptyList() -) : MediaCollectionContent, MediaGroupContent { +) : MediaCollectionContent, VisualMediaGroupContent { override val media: PhotoSize = mediaCollection.biggest() ?: throw IllegalStateException("Can't locate any photo size for this content") override fun createResend( @@ -39,15 +37,10 @@ data class PhotoContent( replyMarkup ) - override fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia = InputMediaPhoto( - media.fileId, + override fun toMediaGroupMemberInputMedia(): InputMediaPhoto = asInputMedia() + + override fun asInputMedia(): InputMediaPhoto = media.toInputMediaPhoto( toHtmlCaptions().firstOrNull(), HTMLParseMode ) - - override fun asInputMedia(): InputMediaPhoto = InputMediaPhoto( - media.fileId, - toMarkdownV2Captions().firstOrNull(), - MarkdownV2 - ) } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/VideoContent.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/VideoContent.kt index 0461d17a63..1049043f37 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/VideoContent.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/content/media/VideoContent.kt @@ -11,8 +11,10 @@ import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2 import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.files.VideoFile +import dev.inmo.tgbotapi.types.files.toInputMediaVideo import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent +import dev.inmo.tgbotapi.types.message.content.abstracts.VisualMediaGroupContent import dev.inmo.tgbotapi.utils.toHtmlCaptions import dev.inmo.tgbotapi.utils.toMarkdownV2Captions @@ -20,7 +22,7 @@ data class VideoContent( override val media: VideoFile, override val caption: String? = null, override val captionEntities: List = emptyList() -) : MediaGroupContent { +) : VisualMediaGroupContent { override fun createResend( chatId: ChatIdentifier, disableNotification: Boolean, @@ -41,23 +43,10 @@ data class VideoContent( replyMarkup ) - override fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia = InputMediaVideo( - media.fileId, - toHtmlCaptions().firstOrNull(), - HTMLParseMode, - media.width, - media.height, - media.duration, - media.thumb ?.fileId - ) + override fun toMediaGroupMemberInputMedia(): InputMediaVideo = asInputMedia() - override fun asInputMedia(): InputMediaVideo = InputMediaVideo( - media.fileId, - toMarkdownV2Captions().firstOrNull(), - MarkdownV2, - media.width, - media.height, - media.duration, - media.thumb ?.fileId + override fun asInputMedia(): InputMediaVideo = media.toInputMediaVideo( + toHtmlCaptions().firstOrNull(), + HTMLParseMode ) } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/Annotations.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/Annotations.kt index 9de1d0cd70..f89e623676 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/Annotations.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/Annotations.kt @@ -17,3 +17,21 @@ package dev.inmo.tgbotapi.utils AnnotationTarget.TYPE_PARAMETER ) annotation class PreviewFeature + +@RequiresOptIn( + "This feature can work unstable and may have some restrictions in Telegram System", + RequiresOptIn.Level.WARNING +) +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.CONSTRUCTOR, + AnnotationTarget.FIELD, + AnnotationTarget.PROPERTY, + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.PROPERTY_SETTER, + AnnotationTarget.FUNCTION, + AnnotationTarget.TYPE, + AnnotationTarget.TYPEALIAS, + AnnotationTarget.TYPE_PARAMETER +) +annotation class RiskFeature(val message: String) diff --git a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/media/SendMediaGroup.kt b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/media/SendMediaGroup.kt index 1f0cf52f6a..03e5d13e3d 100644 --- a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/media/SendMediaGroup.kt +++ b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/media/SendMediaGroup.kt @@ -1,13 +1,18 @@ package dev.inmo.tgbotapi.extensions.api.send.media import dev.inmo.tgbotapi.bot.TelegramBot -import dev.inmo.tgbotapi.requests.send.media.SendMediaGroup +import dev.inmo.tgbotapi.requests.send.media.* import dev.inmo.tgbotapi.types.ChatIdentifier -import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia +import dev.inmo.tgbotapi.types.InputMedia.* import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.chat.abstracts.Chat import dev.inmo.tgbotapi.types.message.abstracts.Message +import dev.inmo.tgbotapi.utils.RiskFeature +/** + * @see SendMediaGroup + */ +@RiskFeature(rawSendingMediaGroupsWarning) suspend fun TelegramBot.sendMediaGroup( chatId: ChatIdentifier, media: List, @@ -19,6 +24,10 @@ suspend fun TelegramBot.sendMediaGroup( ) ) +/** + * @see SendMediaGroup + */ +@RiskFeature(rawSendingMediaGroupsWarning) suspend fun TelegramBot.sendMediaGroup( chat: Chat, media: List, @@ -28,12 +37,108 @@ suspend fun TelegramBot.sendMediaGroup( chat.id, media, disableNotification, replyToMessageId ) +/** + * @see SendPlaylist + */ +suspend fun TelegramBot.sendPlaylist( + chatId: ChatIdentifier, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = execute( + SendPlaylist( + chatId, media, disableNotification, replyToMessageId + ) +) + +/** + * @see SendPlaylist + */ +suspend fun TelegramBot.sendPlaylist( + chat: Chat, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = sendPlaylist( + chat.id, media, disableNotification, replyToMessageId +) + +/** + * @see SendDocumentsGroup + */ +suspend fun TelegramBot.sendDocumentsGroup( + chatId: ChatIdentifier, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = execute( + SendDocumentsGroup( + chatId, media, disableNotification, replyToMessageId + ) +) + +/** + * @see SendDocumentsGroup + */ +suspend fun TelegramBot.sendDocumentsGroup( + chat: Chat, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = sendDocumentsGroup( + chat.id, media, disableNotification, replyToMessageId +) + +/** + * @see SendVisualMediaGroup + */ +suspend fun TelegramBot.sendVisualMediaGroup( + chatId: ChatIdentifier, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = execute( + SendVisualMediaGroup( + chatId, media, disableNotification, replyToMessageId + ) +) + +/** + * @see SendVisualMediaGroup + */ +suspend fun TelegramBot.sendVisualMediaGroup( + chat: Chat, + media: List, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null +) = sendVisualMediaGroup( + chat.id, media, disableNotification, replyToMessageId +) + suspend inline fun TelegramBot.replyWithMediaGroup( to: Message, media: List, disableNotification: Boolean = false ) = sendMediaGroup(to.chat, media, disableNotification, to.messageId) +suspend inline fun TelegramBot.replyWithPlaylist( + to: Message, + media: List, + disableNotification: Boolean = false +) = sendPlaylist(to.chat, media, disableNotification, to.messageId) + +suspend inline fun TelegramBot.replyWithDocumentsGroup( + to: Message, + media: List, + disableNotification: Boolean = false +) = sendDocumentsGroup(to.chat, media, disableNotification, to.messageId) + +suspend inline fun TelegramBot.replyWithVisualMediaGroup( + to: Message, + media: List, + disableNotification: Boolean = false +) = sendVisualMediaGroup(to.chat, media, disableNotification, to.messageId) + suspend inline fun TelegramBot.reply( to: Message, media: List, diff --git a/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/shortcuts/FlowsUpdatesFilter.kt b/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/shortcuts/FlowsUpdatesFilter.kt index 6e08232a35..3c5fe89a8a 100644 --- a/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/shortcuts/FlowsUpdatesFilter.kt +++ b/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/shortcuts/FlowsUpdatesFilter.kt @@ -6,8 +6,7 @@ import dev.inmo.tgbotapi.extensions.utils.updates.asContentMessagesFlow import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage import dev.inmo.tgbotapi.types.message.content.* -import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent -import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent +import dev.inmo.tgbotapi.types.message.content.abstracts.* import dev.inmo.tgbotapi.types.message.content.media.* import dev.inmo.tgbotapi.types.message.payments.InvoiceContent import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.SentMediaGroupUpdate @@ -101,6 +100,12 @@ fun Flow.audioMessages() = filterContentMessages(scopeToIncludeChannels) +fun FlowsUpdatesFilter.audioMessagesWithMediaGroups( + scopeToIncludeChannels: CoroutineScope? = null +) = merge( + filterContentMessages(scopeToIncludeChannels), + mediaGroupAudioMessages(scopeToIncludeChannels).flatMap() +) fun Flow.contactMessages() = filterContentMessages() fun FlowsUpdatesFilter.contactMessages( @@ -116,6 +121,12 @@ fun Flow.documentMessages() = filterContentMessages(scopeToIncludeChannels) +fun FlowsUpdatesFilter.documentMessagesWithMediaGroups( + scopeToIncludeChannels: CoroutineScope? = null +) = merge( + filterContentMessages(scopeToIncludeChannels), + mediaGroupDocumentMessages(scopeToIncludeChannels).flatMap() +) fun Flow.gameMessages() = filterContentMessages() fun FlowsUpdatesFilter.gameMessages( @@ -210,3 +221,18 @@ fun Flow.mediaGroupVideosMessages() = filterMediaGroupMess fun FlowsUpdatesFilter.mediaGroupVideosMessages( scopeToIncludeChannels: CoroutineScope? = null ) = filterMediaGroupMessages(scopeToIncludeChannels) + +fun Flow.mediaGroupVisualMessages() = filterMediaGroupMessages() +fun FlowsUpdatesFilter.mediaGroupVisualMessages( + scopeToIncludeChannels: CoroutineScope? = null +) = filterMediaGroupMessages(scopeToIncludeChannels) + +fun Flow.mediaGroupAudioMessages() = filterMediaGroupMessages() +fun FlowsUpdatesFilter.mediaGroupAudioMessages( + scopeToIncludeChannels: CoroutineScope? = null +) = filterMediaGroupMessages(scopeToIncludeChannels) + +fun Flow.mediaGroupDocumentMessages() = filterMediaGroupMessages() +fun FlowsUpdatesFilter.mediaGroupDocumentMessages( + scopeToIncludeChannels: CoroutineScope? = null +) = filterMediaGroupMessages(scopeToIncludeChannels)