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
## 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
* `Common`:

View File

@ -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

View File

@ -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)

View File

@ -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<InputFile> {
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

View File

@ -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<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>>
= ListSerializer(TelegramBotAPIMessageDeserializeOnlySerializerClass())

View File

@ -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)

View File

@ -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
}

View File

@ -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
)

View File

@ -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
)

View File

@ -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(

View File

@ -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
}

View File

@ -18,3 +18,9 @@ internal fun <T> T.buildArguments(withSerializer: SerializationStrategy<T>) = ar
interface MediaGroupMemberInputMedia : InputMedia, CaptionedOutput {
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) {
is InputMediaPhoto -> 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<MediaGroupMem
return when (json[typeField] ?.jsonPrimitive ?.contentOrNull) {
photoInputMediaType -> 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")
}
}

View File

@ -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) {

View File

@ -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<DiceAnimationType> {
CubeDiceAnimationType.emoji -> CubeDiceAnimationType
DartsDiceAnimationType.emoji -> DartsDiceAnimationType
BasketballDiceAnimationType.emoji -> BasketballDiceAnimationType
SlotMachineDiceAnimationType.emoji -> SlotMachineDiceAnimationType
else -> CustomDiceAnimationType(type)
}
}

View File

@ -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
)

View File

@ -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
)

View File

@ -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,

View File

@ -5,4 +5,8 @@ import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia
interface MediaGroupContent : MediaContent, CaptionedInput {
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
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<TextPart> = 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
)
}

View File

@ -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<TextPart> = 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()
)
}

View File

@ -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<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 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
)
}

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.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<TextPart> = 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
)
}

View File

@ -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)

View File

@ -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<MediaGroupMemberInputMedia>,
@ -19,6 +24,10 @@ suspend fun TelegramBot.sendMediaGroup(
)
)
/**
* @see SendMediaGroup
*/
@RiskFeature(rawSendingMediaGroupsWarning)
suspend fun TelegramBot.sendMediaGroup(
chat: Chat,
media: List<MediaGroupMemberInputMedia>,
@ -28,12 +37,108 @@ suspend fun TelegramBot.sendMediaGroup(
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(
to: Message,
media: List<MediaGroupMemberInputMedia>,
disableNotification: Boolean = false
) = 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(
to: Message,
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.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<BaseSentMessageUpdate>.audioMessages() = filterContentMessages<AudioCon
fun FlowsUpdatesFilter.audioMessages(
scopeToIncludeChannels: CoroutineScope? = null
) = filterContentMessages<AudioContent>(scopeToIncludeChannels)
fun FlowsUpdatesFilter.audioMessagesWithMediaGroups(
scopeToIncludeChannels: CoroutineScope? = null
) = merge(
filterContentMessages<AudioContent>(scopeToIncludeChannels),
mediaGroupAudioMessages(scopeToIncludeChannels).flatMap()
)
fun Flow<BaseSentMessageUpdate>.contactMessages() = filterContentMessages<ContactContent>()
fun FlowsUpdatesFilter.contactMessages(
@ -116,6 +121,12 @@ fun Flow<BaseSentMessageUpdate>.documentMessages() = filterContentMessages<Docum
fun FlowsUpdatesFilter.documentMessages(
scopeToIncludeChannels: CoroutineScope? = null
) = filterContentMessages<DocumentContent>(scopeToIncludeChannels)
fun FlowsUpdatesFilter.documentMessagesWithMediaGroups(
scopeToIncludeChannels: CoroutineScope? = null
) = merge(
filterContentMessages<DocumentContent>(scopeToIncludeChannels),
mediaGroupDocumentMessages(scopeToIncludeChannels).flatMap()
)
fun Flow<BaseSentMessageUpdate>.gameMessages() = filterContentMessages<GameContent>()
fun FlowsUpdatesFilter.gameMessages(
@ -210,3 +221,18 @@ fun Flow<SentMediaGroupUpdate>.mediaGroupVideosMessages() = filterMediaGroupMess
fun FlowsUpdatesFilter.mediaGroupVideosMessages(
scopeToIncludeChannels: CoroutineScope? = null
) = 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)