1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-26 03:58:44 +00:00

Merge pull request #158 from InsanusMokrassar/0.29.3

0.29.3
This commit is contained in:
InsanusMokrassar 2020-11-02 14:21:54 +06:00 committed by GitHub
commit f377d61bf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 418 additions and 91 deletions

View File

@ -1,5 +1,61 @@
# TelegramBotAPI changelog # 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<SentMediaGroupUpdate>#mediaGroupVisualMessages`
* `Flow<SentMediaGroupUpdate>#mediaGroupAudioMessages`
* `Flow<SentMediaGroupUpdate>#mediaGroupDocumentMessages`
* New extensions for `FlowsUpdatesFilter`:
* `FlowsUpdatesFilter#audioMessagesWithMediaGroups`
* `FlowsUpdatesFilter#mediaGroupAudioMessages`
* `FlowsUpdatesFilter#documentMessagesWithMediaGroups`
* `FlowsUpdatesFilter#mediaGroupDocumentMessages`
* `FlowsUpdatesFilter#mediaGroupVisualMessages`
## 0.29.2 ## 0.29.2
* `Common`: * `Common`:

View File

@ -7,7 +7,7 @@ kotlin.incremental.js=true
kotlin_version=1.4.10 kotlin_version=1.4.10
kotlin_coroutines_version=1.4.0 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 klock_version=1.12.1
uuid_version=0.2.2 uuid_version=0.2.2
ktor_version=1.4.1 ktor_version=1.4.1
@ -15,7 +15,7 @@ ktor_version=1.4.1
javax_activation_version=1.1.1 javax_activation_version=1.1.1
library_group=dev.inmo library_group=dev.inmo
library_version=0.29.2 library_version=0.29.3
gradle_bintray_plugin_version=1.8.5 gradle_bintray_plugin_version=1.8.5
github_release_plugin_version=2.2.12 github_release_plugin_version=2.2.12

View File

@ -15,6 +15,7 @@ fun newRequestException(
description.contains("Bad Request: message is not modified") -> MessageIsNotModifiedException(response, plainAnswer, message, cause) description.contains("Bad Request: message is not modified") -> MessageIsNotModifiedException(response, plainAnswer, message, cause)
description == "Unauthorized" -> UnauthorizedException(response, plainAnswer, message, cause) description == "Unauthorized" -> UnauthorizedException(response, plainAnswer, message, cause)
description.contains("PHOTO_INVALID_DIMENSIONS") -> InvalidPhotoDimensionsException(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 else -> null
} }
} ?: CommonRequestException(response, plainAnswer, message, cause) } ?: 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?) : class InvalidPhotoDimensionsException(response: Response, plainAnswer: String, message: String?, cause: Throwable?) :
RequestException(response, plainAnswer, message, cause) RequestException(response, plainAnswer, message, cause)
class WrongFileIdentifierException(response: Response, plainAnswer: String, message: String?, cause: Throwable?) :
RequestException(response, plainAnswer, message, cause)

View File

@ -1,6 +1,5 @@
package dev.inmo.tgbotapi.requests.abstracts package dev.inmo.tgbotapi.requests.abstracts
import dev.inmo.tgbotapi.types.InputMedia.toInputMediaFileAttachmentName
import dev.inmo.tgbotapi.utils.StorageFile import dev.inmo.tgbotapi.utils.StorageFile
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.descriptors.* import kotlinx.serialization.descriptors.*
@ -12,6 +11,14 @@ sealed class InputFile {
abstract val fileId: String 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 // TODO:: add checks for file url/file id regex
/** /**
* Contains file id or file url * Contains file id or file url
@ -30,12 +37,6 @@ internal object InputFileSerializer : KSerializer<InputFile> {
override fun deserialize(decoder: Decoder): FileId = FileId(decoder.decodeString()) 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 // TODO:: add checks for files size
/** /**
* Contains info about file for sending * Contains info about file for sending

View File

@ -8,12 +8,16 @@ import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.InputMedia.* import dev.inmo.tgbotapi.types.InputMedia.*
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializerClass import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializerClass
import dev.inmo.tgbotapi.utils.*
import dev.inmo.tgbotapi.utils.throwRangeError import dev.inmo.tgbotapi.utils.throwRangeError
import dev.inmo.tgbotapi.utils.toJsonWithoutNulls
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.buildJsonArray 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( fun SendMediaGroup(
chatId: ChatIdentifier, chatId: ChatIdentifier,
media: List<MediaGroupMemberInputMedia>, media: List<MediaGroupMemberInputMedia>,
@ -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<AudioMediaGroupMemberInputMedia>,
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<DocumentMediaGroupMemberInputMedia>,
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<VisualMediaGroupMemberInputMedia>,
disableNotification: Boolean = false,
replyToMessageId: MessageIdentifier? = null
) = SendMediaGroup(chatId, media, disableNotification, replyToMessageId)
private val messagesListSerializer: KSerializer<List<MediaGroupMessage>> private val messagesListSerializer: KSerializer<List<MediaGroupMessage>>
= ListSerializer(TelegramBotAPIMessageDeserializeOnlySerializerClass()) = ListSerializer(TelegramBotAPIMessageDeserializeOnlySerializerClass())

View File

@ -3,6 +3,7 @@ package dev.inmo.tgbotapi.types.InputMedia
import dev.inmo.tgbotapi.requests.abstracts.InputFile import dev.inmo.tgbotapi.requests.abstracts.InputFile
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Deprecated("Will be removed due to redundancy for end-side users")
fun String.toInputMediaFileAttachmentName() = "attach://$this" fun String.toInputMediaFileAttachmentName() = "attach://$this"
@Serializable(InputMediaSerializer::class) @Serializable(InputMediaSerializer::class)

View File

@ -23,5 +23,5 @@ data class InputMediaAnimation(
@SerialName(mediaField) @SerialName(mediaField)
override val media: String 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
} }

View File

@ -5,9 +5,13 @@ import dev.inmo.tgbotapi.CommonAbstracts.Performerable
import dev.inmo.tgbotapi.requests.abstracts.* import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.types.ParseMode.ParseMode import dev.inmo.tgbotapi.types.ParseMode.ParseMode
import dev.inmo.tgbotapi.types.ParseMode.parseModeField 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 dev.inmo.tgbotapi.types.mediaField
import kotlinx.serialization.SerialName import dev.inmo.tgbotapi.types.message.content.media.AudioContent
import kotlinx.serialization.Serializable import kotlinx.serialization.*
internal const val audioInputMediaType = "audio"
@Serializable @Serializable
data class InputMediaAudio( data class InputMediaAudio(
@ -19,10 +23,26 @@ data class InputMediaAudio(
override val performer: String? = null, override val performer: String? = null,
override val title: String? = null, override val title: String? = null,
override val thumb: InputFile? = null override val thumb: InputFile? = null
) : InputMedia, DuratedInputMedia, ThumbedInputMedia, TitledInputMedia, CaptionedOutput, Performerable { ) : InputMedia, AudioMediaGroupMemberInputMedia, DuratedInputMedia, ThumbedInputMedia, TitledInputMedia, CaptionedOutput, Performerable {
override val type: String = "audio" override val type: String = audioInputMediaType
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
@SerialName(mediaField) @SerialName(mediaField)
override val media: String 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
)

View File

@ -4,9 +4,11 @@ import dev.inmo.tgbotapi.CommonAbstracts.CaptionedOutput
import dev.inmo.tgbotapi.requests.abstracts.* import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.types.ParseMode.ParseMode import dev.inmo.tgbotapi.types.ParseMode.ParseMode
import dev.inmo.tgbotapi.types.ParseMode.parseModeField import dev.inmo.tgbotapi.types.ParseMode.parseModeField
import dev.inmo.tgbotapi.types.files.DocumentFile
import dev.inmo.tgbotapi.types.mediaField import dev.inmo.tgbotapi.types.mediaField
import kotlinx.serialization.SerialName import kotlinx.serialization.*
import kotlinx.serialization.Serializable
internal const val documentInputMediaType = "document"
@Serializable @Serializable
data class InputMediaDocument( data class InputMediaDocument(
@ -15,10 +17,22 @@ data class InputMediaDocument(
@SerialName(parseModeField) @SerialName(parseModeField)
override val parseMode: ParseMode? = null, override val parseMode: ParseMode? = null,
override val thumb: InputFile? = null override val thumb: InputFile? = null
) : InputMedia, ThumbedInputMedia, CaptionedOutput { ) : InputMedia, DocumentMediaGroupMemberInputMedia, ThumbedInputMedia, CaptionedOutput {
override val type: String = "document" override val type: String = documentInputMediaType
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
@SerialName(mediaField) @SerialName(mediaField)
override val media: String 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
)

View File

@ -17,14 +17,14 @@ data class InputMediaPhoto(
override val caption: String? = null, override val caption: String? = null,
@SerialName(parseModeField) @SerialName(parseModeField)
override val parseMode: ParseMode? = null override val parseMode: ParseMode? = null
) : InputMedia, MediaGroupMemberInputMedia { ) : InputMedia, VisualMediaGroupMemberInputMedia {
override val type: String = photoInputMediaType override val type: String = photoInputMediaType
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
@SerialName(mediaField) @SerialName(mediaField)
override val media: String 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( fun PhotoSize.toInputMediaPhoto(

View File

@ -19,12 +19,12 @@ data class InputMediaVideo(
override val height: Int? = null, override val height: Int? = null,
override val duration: Long? = null, override val duration: Long? = null,
override val thumb: InputFile? = null override val thumb: InputFile? = null
) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, MediaGroupMemberInputMedia { ) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, VisualMediaGroupMemberInputMedia {
override val type: String = videoInputMediaType override val type: String = videoInputMediaType
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this) override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
@SerialName(mediaField) @SerialName(mediaField)
override val media: String 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
} }

View File

@ -18,3 +18,9 @@ internal fun <T> T.buildArguments(withSerializer: SerializationStrategy<T>) = ar
interface MediaGroupMemberInputMedia : InputMedia, CaptionedOutput { interface MediaGroupMemberInputMedia : InputMedia, CaptionedOutput {
fun serialize(format: StringFormat): String fun serialize(format: StringFormat): String
} }
interface AudioMediaGroupMemberInputMedia: MediaGroupMemberInputMedia
interface DocumentMediaGroupMemberInputMedia: MediaGroupMemberInputMedia
@Serializable(MediaGroupMemberInputMediaSerializer::class)
interface VisualMediaGroupMemberInputMedia : MediaGroupMemberInputMedia

View File

@ -16,6 +16,8 @@ internal object MediaGroupMemberInputMediaSerializer : KSerializer<MediaGroupMem
when (value) { when (value) {
is InputMediaPhoto -> InputMediaPhoto.serializer().serialize(encoder, value) is InputMediaPhoto -> InputMediaPhoto.serializer().serialize(encoder, value)
is InputMediaVideo -> InputMediaVideo.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<MediaGroupMem
return when (json[typeField] ?.jsonPrimitive ?.contentOrNull) { return when (json[typeField] ?.jsonPrimitive ?.contentOrNull) {
photoInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaPhoto.serializer(), json) photoInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaPhoto.serializer(), json)
videoInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaVideo.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") else -> error("Illegal type of incoming MediaGroupMemberInputMedia")
} }
} }

View File

@ -9,6 +9,7 @@ interface ThumbedInputMedia : InputMedia {
val thumb: InputFile? val thumb: InputFile?
@Serializable @Serializable
@SerialName(thumbField) @SerialName(thumbField)
@Deprecated("Will be removed due to useless state")
val thumbMedia: String? val thumbMedia: String?
get() = thumb ?.let { get() = thumb ?.let {
when (it) { when (it) {

View File

@ -22,6 +22,10 @@ object BasketballDiceAnimationType : DiceAnimationType() {
override val emoji: String = "\uD83C\uDFC0" override val emoji: String = "\uD83C\uDFC0"
} }
@Serializable(DiceAnimationTypeSerializer::class) @Serializable(DiceAnimationTypeSerializer::class)
object SlotMachineDiceAnimationType : DiceAnimationType() {
override val emoji: String = "\uD83C\uDFB0"
}
@Serializable(DiceAnimationTypeSerializer::class)
data class CustomDiceAnimationType( data class CustomDiceAnimationType(
override val emoji: String override val emoji: String
) : DiceAnimationType() ) : DiceAnimationType()
@ -34,6 +38,7 @@ internal object DiceAnimationTypeSerializer : KSerializer<DiceAnimationType> {
CubeDiceAnimationType.emoji -> CubeDiceAnimationType CubeDiceAnimationType.emoji -> CubeDiceAnimationType
DartsDiceAnimationType.emoji -> DartsDiceAnimationType DartsDiceAnimationType.emoji -> DartsDiceAnimationType
BasketballDiceAnimationType.emoji -> BasketballDiceAnimationType BasketballDiceAnimationType.emoji -> BasketballDiceAnimationType
SlotMachineDiceAnimationType.emoji -> SlotMachineDiceAnimationType
else -> CustomDiceAnimationType(type) else -> CustomDiceAnimationType(type)
} }
} }

View File

@ -22,3 +22,13 @@ data class DocumentFile(
@SerialName(fileNameField) @SerialName(fileNameField)
override val fileName: String? = null override val fileName: String? = null
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, CustomNamedMediaFile ) : 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
)

View File

@ -2,9 +2,13 @@ package dev.inmo.tgbotapi.types.files
import dev.inmo.tgbotapi.requests.abstracts.FileId import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.FileUniqueId 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.fileUniqueIdField
import dev.inmo.tgbotapi.types.files.abstracts.* import dev.inmo.tgbotapi.types.files.abstracts.*
import dev.inmo.tgbotapi.utils.MimeType import dev.inmo.tgbotapi.utils.MimeType
import dev.inmo.tgbotapi.utils.toHtmlCaptions
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -23,3 +27,17 @@ data class VideoFile(
@SerialName(fileSizeField) @SerialName(fileSizeField)
override val fileSize: Long? = null override val fileSize: Long? = null
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, SizedMediaFile ) : 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
)

View File

@ -220,17 +220,20 @@ internal data class RawMessage(
} }
} ?: content?.let { content -> } ?: content?.let { content ->
media_group_id?.let { 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) { when (from) {
null -> ChannelMediaGroupMessage( null -> ChannelMediaGroupMessage(
messageId, messageId,
chat, chat,
date.asDate, date.asDate,
it, it,
when (content) { checkedContent,
is PhotoContent -> content
is VideoContent -> content
else -> error("Unsupported content for media group")
},
edit_date?.asDate, edit_date?.asDate,
forwarded, forwarded,
reply_to_message?.asMessage, reply_to_message?.asMessage,
@ -242,11 +245,7 @@ internal data class RawMessage(
chat, chat,
date.asDate, date.asDate,
it, it,
when (content) { checkedContent,
is PhotoContent -> content
is VideoContent -> content
else -> error("Unsupported content for media group")
},
edit_date?.asDate, edit_date?.asDate,
forwarded, forwarded,
reply_to_message?.asMessage, reply_to_message?.asMessage,

View File

@ -6,3 +6,7 @@ import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia
interface MediaGroupContent : MediaContent, CaptionedInput { interface MediaGroupContent : MediaContent, CaptionedInput {
fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia
} }
interface VisualMediaGroupContent : MediaGroupContent
interface AudioMediaGroupContent : MediaGroupContent
interface DocumentMediaGroupContent : MediaGroupContent

View File

@ -1,18 +1,17 @@
package dev.inmo.tgbotapi.types.message.content.media package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.CommonAbstracts.CaptionedInput
import dev.inmo.tgbotapi.CommonAbstracts.TextPart import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendAudio import dev.inmo.tgbotapi.requests.send.media.SendAudio
import dev.inmo.tgbotapi.types.ChatIdentifier 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.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2 import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.AudioFile import dev.inmo.tgbotapi.types.files.AudioFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage 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.toHtmlCaptions
import dev.inmo.tgbotapi.utils.toMarkdownV2Captions import dev.inmo.tgbotapi.utils.toMarkdownV2Captions
@ -20,7 +19,7 @@ data class AudioContent(
override val media: AudioFile, override val media: AudioFile,
override val caption: String? = null, override val caption: String? = null,
override val captionEntities: List<TextPart> = emptyList() override val captionEntities: List<TextPart> = emptyList()
) : MediaContent, CaptionedInput { ) : AudioMediaGroupContent {
override fun createResend( override fun createResend(
chatId: ChatIdentifier, chatId: ChatIdentifier,
disableNotification: Boolean, disableNotification: Boolean,
@ -40,13 +39,10 @@ data class AudioContent(
replyMarkup replyMarkup
) )
override fun asInputMedia(): InputMediaAudio = InputMediaAudio( override fun toMediaGroupMemberInputMedia(): InputMediaAudio = asInputMedia()
media.fileId,
toMarkdownV2Captions().firstOrNull(), override fun asInputMedia(): InputMediaAudio = media.toInputMediaAudio(
MarkdownV2, toHtmlCaptions().firstOrNull(),
media.duration, HTMLParseMode
media.performer,
media.title,
media.thumb ?.fileId
) )
} }

View File

@ -5,14 +5,15 @@ import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendDocument import dev.inmo.tgbotapi.requests.send.media.SendDocument
import dev.inmo.tgbotapi.types.ChatIdentifier 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.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2 import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.DocumentFile 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.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.toHtmlCaptions
import dev.inmo.tgbotapi.utils.toMarkdownV2Captions import dev.inmo.tgbotapi.utils.toMarkdownV2Captions
@ -20,7 +21,7 @@ data class DocumentContent(
override val media: DocumentFile, override val media: DocumentFile,
override val caption: String? = null, override val caption: String? = null,
override val captionEntities: List<TextPart> = emptyList() override val captionEntities: List<TextPart> = emptyList()
) : MediaContent, CaptionedInput { ) : DocumentMediaGroupContent {
override fun createResend( override fun createResend(
chatId: ChatIdentifier, chatId: ChatIdentifier,
disableNotification: Boolean, disableNotification: Boolean,
@ -37,10 +38,22 @@ data class DocumentContent(
replyMarkup replyMarkup
) )
override fun asInputMedia(): InputMediaDocument = InputMediaDocument( override fun toMediaGroupMemberInputMedia(): InputMediaDocument = asInputMedia()
media.fileId,
toMarkdownV2Captions().firstOrNull(), override fun asInputMedia(): InputMediaDocument = media.toInputMediaDocument(
MarkdownV2, toHtmlCaptions().firstOrNull(),
media.thumb ?.fileId HTMLParseMode
)
}
@Suppress("NOTHING_TO_INLINE")
inline fun MediaContent.asDocumentContent() = when (this) {
is CaptionedInput -> DocumentContent(
media.asDocumentFile(),
caption,
captionEntities
)
else -> DocumentContent(
media.asDocumentFile()
) )
} }

View File

@ -4,16 +4,14 @@ import dev.inmo.tgbotapi.CommonAbstracts.TextPart
import dev.inmo.tgbotapi.requests.abstracts.Request import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendPhoto import dev.inmo.tgbotapi.requests.send.media.SendPhoto
import dev.inmo.tgbotapi.types.ChatIdentifier import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.InputMediaPhoto import dev.inmo.tgbotapi.types.InputMedia.*
import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia
import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2 import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.* import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage 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.*
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.utils.toHtmlCaptions import dev.inmo.tgbotapi.utils.toHtmlCaptions
import dev.inmo.tgbotapi.utils.toMarkdownV2Captions import dev.inmo.tgbotapi.utils.toMarkdownV2Captions
@ -21,7 +19,7 @@ data class PhotoContent(
override val mediaCollection: Photo, override val mediaCollection: Photo,
override val caption: String? = null, override val caption: String? = null,
override val captionEntities: List<TextPart> = emptyList() override val captionEntities: List<TextPart> = emptyList()
) : MediaCollectionContent<PhotoSize>, MediaGroupContent { ) : MediaCollectionContent<PhotoSize>, VisualMediaGroupContent {
override val media: PhotoSize = mediaCollection.biggest() ?: throw IllegalStateException("Can't locate any photo size for this content") override val media: PhotoSize = mediaCollection.biggest() ?: throw IllegalStateException("Can't locate any photo size for this content")
override fun createResend( override fun createResend(
@ -39,15 +37,10 @@ data class PhotoContent(
replyMarkup replyMarkup
) )
override fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia = InputMediaPhoto( override fun toMediaGroupMemberInputMedia(): InputMediaPhoto = asInputMedia()
media.fileId,
override fun asInputMedia(): InputMediaPhoto = media.toInputMediaPhoto(
toHtmlCaptions().firstOrNull(), toHtmlCaptions().firstOrNull(),
HTMLParseMode HTMLParseMode
) )
override fun asInputMedia(): InputMediaPhoto = InputMediaPhoto(
media.fileId,
toMarkdownV2Captions().firstOrNull(),
MarkdownV2
)
} }

View File

@ -11,8 +11,10 @@ import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2 import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.VideoFile 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.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent 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.toHtmlCaptions
import dev.inmo.tgbotapi.utils.toMarkdownV2Captions import dev.inmo.tgbotapi.utils.toMarkdownV2Captions
@ -20,7 +22,7 @@ data class VideoContent(
override val media: VideoFile, override val media: VideoFile,
override val caption: String? = null, override val caption: String? = null,
override val captionEntities: List<TextPart> = emptyList() override val captionEntities: List<TextPart> = emptyList()
) : MediaGroupContent { ) : VisualMediaGroupContent {
override fun createResend( override fun createResend(
chatId: ChatIdentifier, chatId: ChatIdentifier,
disableNotification: Boolean, disableNotification: Boolean,
@ -41,23 +43,10 @@ data class VideoContent(
replyMarkup replyMarkup
) )
override fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia = InputMediaVideo( override fun toMediaGroupMemberInputMedia(): InputMediaVideo = asInputMedia()
media.fileId,
toHtmlCaptions().firstOrNull(),
HTMLParseMode,
media.width,
media.height,
media.duration,
media.thumb ?.fileId
)
override fun asInputMedia(): InputMediaVideo = InputMediaVideo( override fun asInputMedia(): InputMediaVideo = media.toInputMediaVideo(
media.fileId, toHtmlCaptions().firstOrNull(),
toMarkdownV2Captions().firstOrNull(), HTMLParseMode
MarkdownV2,
media.width,
media.height,
media.duration,
media.thumb ?.fileId
) )
} }

View File

@ -17,3 +17,21 @@ package dev.inmo.tgbotapi.utils
AnnotationTarget.TYPE_PARAMETER AnnotationTarget.TYPE_PARAMETER
) )
annotation class PreviewFeature 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)

View File

@ -1,13 +1,18 @@
package dev.inmo.tgbotapi.extensions.api.send.media package dev.inmo.tgbotapi.extensions.api.send.media
import dev.inmo.tgbotapi.bot.TelegramBot 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.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.MessageIdentifier
import dev.inmo.tgbotapi.types.chat.abstracts.Chat import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.message.abstracts.Message import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.utils.RiskFeature
/**
* @see SendMediaGroup
*/
@RiskFeature(rawSendingMediaGroupsWarning)
suspend fun TelegramBot.sendMediaGroup( suspend fun TelegramBot.sendMediaGroup(
chatId: ChatIdentifier, chatId: ChatIdentifier,
media: List<MediaGroupMemberInputMedia>, media: List<MediaGroupMemberInputMedia>,
@ -19,6 +24,10 @@ suspend fun TelegramBot.sendMediaGroup(
) )
) )
/**
* @see SendMediaGroup
*/
@RiskFeature(rawSendingMediaGroupsWarning)
suspend fun TelegramBot.sendMediaGroup( suspend fun TelegramBot.sendMediaGroup(
chat: Chat, chat: Chat,
media: List<MediaGroupMemberInputMedia>, media: List<MediaGroupMemberInputMedia>,
@ -28,12 +37,108 @@ suspend fun TelegramBot.sendMediaGroup(
chat.id, media, disableNotification, replyToMessageId chat.id, media, disableNotification, replyToMessageId
) )
/**
* @see SendPlaylist
*/
suspend fun TelegramBot.sendPlaylist(
chatId: ChatIdentifier,
media: List<AudioMediaGroupMemberInputMedia>,
disableNotification: Boolean = false,
replyToMessageId: MessageIdentifier? = null
) = execute(
SendPlaylist(
chatId, media, disableNotification, replyToMessageId
)
)
/**
* @see SendPlaylist
*/
suspend fun TelegramBot.sendPlaylist(
chat: Chat,
media: List<AudioMediaGroupMemberInputMedia>,
disableNotification: Boolean = false,
replyToMessageId: MessageIdentifier? = null
) = sendPlaylist(
chat.id, media, disableNotification, replyToMessageId
)
/**
* @see SendDocumentsGroup
*/
suspend fun TelegramBot.sendDocumentsGroup(
chatId: ChatIdentifier,
media: List<DocumentMediaGroupMemberInputMedia>,
disableNotification: Boolean = false,
replyToMessageId: MessageIdentifier? = null
) = execute(
SendDocumentsGroup(
chatId, media, disableNotification, replyToMessageId
)
)
/**
* @see SendDocumentsGroup
*/
suspend fun TelegramBot.sendDocumentsGroup(
chat: Chat,
media: List<DocumentMediaGroupMemberInputMedia>,
disableNotification: Boolean = false,
replyToMessageId: MessageIdentifier? = null
) = sendDocumentsGroup(
chat.id, media, disableNotification, replyToMessageId
)
/**
* @see SendVisualMediaGroup
*/
suspend fun TelegramBot.sendVisualMediaGroup(
chatId: ChatIdentifier,
media: List<VisualMediaGroupMemberInputMedia>,
disableNotification: Boolean = false,
replyToMessageId: MessageIdentifier? = null
) = execute(
SendVisualMediaGroup(
chatId, media, disableNotification, replyToMessageId
)
)
/**
* @see SendVisualMediaGroup
*/
suspend fun TelegramBot.sendVisualMediaGroup(
chat: Chat,
media: List<VisualMediaGroupMemberInputMedia>,
disableNotification: Boolean = false,
replyToMessageId: MessageIdentifier? = null
) = sendVisualMediaGroup(
chat.id, media, disableNotification, replyToMessageId
)
suspend inline fun TelegramBot.replyWithMediaGroup( suspend inline fun TelegramBot.replyWithMediaGroup(
to: Message, to: Message,
media: List<MediaGroupMemberInputMedia>, media: List<MediaGroupMemberInputMedia>,
disableNotification: Boolean = false disableNotification: Boolean = false
) = sendMediaGroup(to.chat, media, disableNotification, to.messageId) ) = sendMediaGroup(to.chat, media, disableNotification, to.messageId)
suspend inline fun TelegramBot.replyWithPlaylist(
to: Message,
media: List<AudioMediaGroupMemberInputMedia>,
disableNotification: Boolean = false
) = sendPlaylist(to.chat, media, disableNotification, to.messageId)
suspend inline fun TelegramBot.replyWithDocumentsGroup(
to: Message,
media: List<DocumentMediaGroupMemberInputMedia>,
disableNotification: Boolean = false
) = sendDocumentsGroup(to.chat, media, disableNotification, to.messageId)
suspend inline fun TelegramBot.replyWithVisualMediaGroup(
to: Message,
media: List<VisualMediaGroupMemberInputMedia>,
disableNotification: Boolean = false
) = sendVisualMediaGroup(to.chat, media, disableNotification, to.messageId)
suspend inline fun TelegramBot.reply( suspend inline fun TelegramBot.reply(
to: Message, to: Message,
media: List<MediaGroupMemberInputMedia>, media: List<MediaGroupMemberInputMedia>,

View File

@ -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.CommonMessage
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.* import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent
import dev.inmo.tgbotapi.types.message.content.media.* import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.SentMediaGroupUpdate import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.SentMediaGroupUpdate
@ -101,6 +100,12 @@ fun Flow<BaseSentMessageUpdate>.audioMessages() = filterContentMessages<AudioCon
fun FlowsUpdatesFilter.audioMessages( fun FlowsUpdatesFilter.audioMessages(
scopeToIncludeChannels: CoroutineScope? = null scopeToIncludeChannels: CoroutineScope? = null
) = filterContentMessages<AudioContent>(scopeToIncludeChannels) ) = filterContentMessages<AudioContent>(scopeToIncludeChannels)
fun FlowsUpdatesFilter.audioMessagesWithMediaGroups(
scopeToIncludeChannels: CoroutineScope? = null
) = merge(
filterContentMessages<AudioContent>(scopeToIncludeChannels),
mediaGroupAudioMessages(scopeToIncludeChannels).flatMap()
)
fun Flow<BaseSentMessageUpdate>.contactMessages() = filterContentMessages<ContactContent>() fun Flow<BaseSentMessageUpdate>.contactMessages() = filterContentMessages<ContactContent>()
fun FlowsUpdatesFilter.contactMessages( fun FlowsUpdatesFilter.contactMessages(
@ -116,6 +121,12 @@ fun Flow<BaseSentMessageUpdate>.documentMessages() = filterContentMessages<Docum
fun FlowsUpdatesFilter.documentMessages( fun FlowsUpdatesFilter.documentMessages(
scopeToIncludeChannels: CoroutineScope? = null scopeToIncludeChannels: CoroutineScope? = null
) = filterContentMessages<DocumentContent>(scopeToIncludeChannels) ) = filterContentMessages<DocumentContent>(scopeToIncludeChannels)
fun FlowsUpdatesFilter.documentMessagesWithMediaGroups(
scopeToIncludeChannels: CoroutineScope? = null
) = merge(
filterContentMessages<DocumentContent>(scopeToIncludeChannels),
mediaGroupDocumentMessages(scopeToIncludeChannels).flatMap()
)
fun Flow<BaseSentMessageUpdate>.gameMessages() = filterContentMessages<GameContent>() fun Flow<BaseSentMessageUpdate>.gameMessages() = filterContentMessages<GameContent>()
fun FlowsUpdatesFilter.gameMessages( fun FlowsUpdatesFilter.gameMessages(
@ -210,3 +221,18 @@ fun Flow<SentMediaGroupUpdate>.mediaGroupVideosMessages() = filterMediaGroupMess
fun FlowsUpdatesFilter.mediaGroupVideosMessages( fun FlowsUpdatesFilter.mediaGroupVideosMessages(
scopeToIncludeChannels: CoroutineScope? = null scopeToIncludeChannels: CoroutineScope? = null
) = filterMediaGroupMessages<VideoContent>(scopeToIncludeChannels) ) = filterMediaGroupMessages<VideoContent>(scopeToIncludeChannels)
fun Flow<SentMediaGroupUpdate>.mediaGroupVisualMessages() = filterMediaGroupMessages<VisualMediaGroupContent>()
fun FlowsUpdatesFilter.mediaGroupVisualMessages(
scopeToIncludeChannels: CoroutineScope? = null
) = filterMediaGroupMessages<VisualMediaGroupContent>(scopeToIncludeChannels)
fun Flow<SentMediaGroupUpdate>.mediaGroupAudioMessages() = filterMediaGroupMessages<AudioContent>()
fun FlowsUpdatesFilter.mediaGroupAudioMessages(
scopeToIncludeChannels: CoroutineScope? = null
) = filterMediaGroupMessages<AudioContent>(scopeToIncludeChannels)
fun Flow<SentMediaGroupUpdate>.mediaGroupDocumentMessages() = filterMediaGroupMessages<DocumentContent>()
fun FlowsUpdatesFilter.mediaGroupDocumentMessages(
scopeToIncludeChannels: CoroutineScope? = null
) = filterMediaGroupMessages<DocumentContent>(scopeToIncludeChannels)