1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2026-06-18 09:15:10 +00:00

add LivePhotos support

This commit is contained in:
2026-05-17 16:43:18 +06:00
parent f3f28b6165
commit d47641c39a
41 changed files with 2135 additions and 13 deletions

View File

@@ -0,0 +1,211 @@
@file:Suppress("FunctionName")
package dev.inmo.tgbotapi.requests.send.media
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.send.abstracts.*
import dev.inmo.tgbotapi.requests.send.media.base.*
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.types.message.parseModeField
import dev.inmo.tgbotapi.types.message.*
import dev.inmo.tgbotapi.types.message.RawMessageEntity
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializationStrategyClass
import dev.inmo.tgbotapi.types.message.content.LivePhotoContent
import dev.inmo.tgbotapi.types.message.toRawMessageEntities
import dev.inmo.tgbotapi.utils.extensions.makeString
import dev.inmo.tgbotapi.utils.mapOfNotNull
import dev.inmo.tgbotapi.utils.throwRangeError
import kotlinx.serialization.*
fun SendLivePhoto(
chatId: ChatIdentifier,
livePhoto: InputFile,
photo: InputFile,
text: String? = null,
parseMode: ParseMode? = null,
showCaptionAboveMedia: Boolean = false,
spoilered: Boolean = false,
threadId: MessageThreadId? = chatId.threadId,
directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
businessConnectionId: BusinessConnectionId? = chatId.businessConnectionId,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowPaidBroadcast: Boolean = false,
effectId: EffectId? = null,
suggestedPostParameters: SuggestedPostParameters? = null,
replyParameters: ReplyParameters? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<LivePhotoContent>> {
val livePhotoAsFile = livePhoto as? MultipartFile
val photoAsFile = photo as? MultipartFile
val data = SendLivePhotoData(
chatId = chatId,
livePhoto = livePhoto,
photo = photo,
text = text,
parseMode = parseMode,
rawEntities = null,
showCaptionAboveMedia = showCaptionAboveMedia,
spoilered = spoilered,
threadId = threadId,
directMessageThreadId = directMessageThreadId,
businessConnectionId = businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
allowPaidBroadcast = allowPaidBroadcast,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)
return if (livePhotoAsFile == null && photoAsFile == null) {
data
} else {
CommonMultipartFileRequest(
data,
listOfNotNull(livePhotoAsFile, photoAsFile).associateBy { it.fileId }
)
}
}
fun SendLivePhoto(
chatId: ChatIdentifier,
livePhoto: InputFile,
photo: InputFile,
entities: TextSourcesList,
showCaptionAboveMedia: Boolean = false,
spoilered: Boolean = false,
threadId: MessageThreadId? = chatId.threadId,
directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
businessConnectionId: BusinessConnectionId? = chatId.businessConnectionId,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowPaidBroadcast: Boolean = false,
effectId: EffectId? = null,
suggestedPostParameters: SuggestedPostParameters? = null,
replyParameters: ReplyParameters? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<LivePhotoContent>> {
val livePhotoAsFile = livePhoto as? MultipartFile
val photoAsFile = photo as? MultipartFile
val data = SendLivePhotoData(
chatId = chatId,
livePhoto = livePhoto,
photo = photo,
text = entities.makeString(),
parseMode = null,
rawEntities = entities.toRawMessageEntities(),
showCaptionAboveMedia = showCaptionAboveMedia,
spoilered = spoilered,
threadId = threadId,
directMessageThreadId = directMessageThreadId,
businessConnectionId = businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
allowPaidBroadcast = allowPaidBroadcast,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)
return if (livePhotoAsFile == null && photoAsFile == null) {
data
} else {
CommonMultipartFileRequest(
data,
listOfNotNull(livePhotoAsFile, photoAsFile).associateBy { it.fileId }
)
}
}
private val commonResultDeserializer: DeserializationStrategy<ContentMessage<LivePhotoContent>>
= TelegramBotAPIMessageDeserializationStrategyClass()
@ConsistentCopyVisibility
@Serializable
data class SendLivePhotoData internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(livePhotoField)
val livePhoto: InputFile,
@SerialName(photoField)
val photo: InputFile,
@SerialName(captionField)
override val text: String? = null,
@SerialName(parseModeField)
override val parseMode: ParseMode? = null,
@SerialName(captionEntitiesField)
private val rawEntities: List<RawMessageEntity>? = null,
@SerialName(showCaptionAboveMediaField)
override val showCaptionAboveMedia: Boolean = false,
@SerialName(hasSpoilerField)
override val spoilered: Boolean = false,
@OptIn(ExperimentalSerializationApi::class)
@SerialName(messageThreadIdField)
@EncodeDefault
override val threadId: MessageThreadId? = chatId.threadId,
@OptIn(ExperimentalSerializationApi::class)
@EncodeDefault
@SerialName(directMessagesTopicIdField)
override val directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
@SerialName(businessConnectionIdField)
override val businessConnectionId: BusinessConnectionId? = chatId.businessConnectionId,
@SerialName(disableNotificationField)
override val disableNotification: Boolean = false,
@SerialName(protectContentField)
override val protectContent: Boolean = false,
@SerialName(allowPaidBroadcastField)
override val allowPaidBroadcast: Boolean = false,
@SerialName(messageEffectIdField)
override val effectId: EffectId? = null,
@SerialName(suggestedPostParametersField)
override val suggestedPostParameters: SuggestedPostParameters? = null,
@SerialName(replyParametersField)
override val replyParameters: ReplyParameters? = null,
@SerialName(replyMarkupField)
override val replyMarkup: KeyboardMarkup? = null
) : DataRequest<ContentMessage<LivePhotoContent>>,
SendContentMessageRequest<ContentMessage<LivePhotoContent>>,
ReplyingMarkupSendMessageRequest<ContentMessage<LivePhotoContent>>,
TextableSendMessageRequest<ContentMessage<LivePhotoContent>>,
WithCustomizableCaptionRequest<ContentMessage<LivePhotoContent>>,
OptionallyWithSpoilerRequest
{
override val textSources: TextSourcesList? by lazy {
rawEntities ?.asTextSources(text ?: return@lazy null)
}
init {
text ?.let {
if (it.length !in captionLength) {
throwRangeError("Caption length", captionLength, it.length)
}
}
}
override fun method(): String = "sendLivePhoto"
override val resultDeserializer: DeserializationStrategy<ContentMessage<LivePhotoContent>>
get() = commonResultDeserializer
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}
@Suppress("unused")
@ConsistentCopyVisibility
data class SendLivePhotoFiles internal constructor(
val livePhoto: MultipartFile? = null,
val photo: MultipartFile? = null
) : Files by mapOfNotNull(
livePhotoField to livePhoto,
photoField to photo
)

View File

@@ -697,6 +697,7 @@ const val documentField = "document"
const val photoField = "photo"
const val audioField = "audio"
const val videoField = "video"
const val livePhotoField = "live_photo"
const val animationField = "animation"
const val venueField = "venue"
const val voiceField = "voice"

View File

@@ -120,6 +120,7 @@ sealed interface ReplyInfo {
private val photo: PhotoFile? = null,
private val sticker: Sticker? = null,
private val video: VideoFile? = null,
private val live_photo: LivePhotoFile? = null,
private val voice: VoiceFile? = null,
private val video_note: VideoNoteFile? = null,
private val contact: Contact? = null,
@@ -146,6 +147,7 @@ sealed interface ReplyInfo {
story != null -> story
audio != null -> audio
video != null -> video
live_photo != null -> live_photo
video_note != null -> video_note
animation != null -> animation
document != null -> document

View File

@@ -0,0 +1,74 @@
package dev.inmo.tgbotapi.types.files
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.media.PollMedia
import dev.inmo.tgbotapi.types.media.TelegramMediaLivePhoto
import dev.inmo.tgbotapi.types.media.TelegramPaidMediaLivePhoto
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.utils.MimeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class LivePhotoFile(
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@SerialName(fileIdField)
override val fileId: FileId,
@SerialName(fileUniqueIdField)
override val fileUniqueId: TgFileUniqueId,
@SerialName(widthField)
override val width: Int,
@SerialName(heightField)
override val height: Int,
@SerialName(durationField)
override val duration: Long,
@Serializable(PhotoSerializer::class)
@SerialName(photoField)
val photo: PhotoFile? = null,
@SerialName(mimeTypeField)
override val mimeType: MimeType? = null,
@SerialName(fileSizeField)
override val fileSize: FileSize? = null
) : TelegramMediaFile,
PollMedia,
MimedMediaFile,
PlayableMediaFile,
SizedMediaFile,
MediaContentVariant,
UsefulAsPaidMediaFile
@Suppress("NOTHING_TO_INLINE")
inline fun LivePhotoFile.toTelegramMediaLivePhoto(
text: String? = null,
parseMode: ParseMode? = null,
spoilered: Boolean = false,
showCaptionAboveMedia: Boolean = false
) = TelegramMediaLivePhoto(
file = fileId,
photo = photo ?.fileId ?: fileId,
text = text,
parseMode = parseMode,
spoilered = spoilered,
showCaptionAboveMedia = showCaptionAboveMedia
)
@Suppress("NOTHING_TO_INLINE")
inline fun LivePhotoFile.toTelegramMediaLivePhoto(
textSources: TextSourcesList,
spoilered: Boolean = false,
showCaptionAboveMedia: Boolean = false
) = TelegramMediaLivePhoto(
file = fileId,
photo = photo ?.fileId ?: fileId,
entities = textSources,
spoilered = spoilered,
showCaptionAboveMedia = showCaptionAboveMedia
)
@Suppress("NOTHING_TO_INLINE")
inline fun LivePhotoFile.toTelegramPaidMediaLivePhoto() = TelegramPaidMediaLivePhoto(
file = fileId,
photo = photo ?.fileId ?: fileId
)

View File

@@ -29,7 +29,7 @@ object InputPollMediaSerializer : KSerializer<InputPollMedia> {
is TelegramMediaAnimation -> TelegramMediaAnimation.serializer().serialize(encoder, value)
is TelegramMediaAudio -> TelegramMediaAudio.serializer().serialize(encoder, value)
is TelegramMediaDocument -> TelegramMediaDocument.serializer().serialize(encoder, value)
// TODO::ADD TelegramMediaLivePhoto
is TelegramMediaLivePhoto -> TelegramMediaLivePhoto.serializer().serialize(encoder, value)
is TelegramMediaLocation -> TelegramMediaLocation.serializer().serialize(encoder, value)
is TelegramMediaPhoto -> TelegramMediaPhoto.serializer().serialize(encoder, value)
is TelegramMediaVenue -> TelegramMediaVenue.serializer().serialize(encoder, value)

View File

@@ -27,7 +27,7 @@ object InputPollOptionMediaSerializer : KSerializer<InputPollOptionMedia> {
override fun serialize(encoder: Encoder, value: InputPollOptionMedia) {
when (value) {
is TelegramMediaAnimation -> TelegramMediaAnimation.serializer().serialize(encoder, value)
// TODO::Add TelegramMediaLivePhoto
is TelegramMediaLivePhoto -> TelegramMediaLivePhoto.serializer().serialize(encoder, value)
is TelegramMediaLocation -> TelegramMediaLocation.serializer().serialize(encoder, value)
is TelegramMediaPhoto -> TelegramMediaPhoto.serializer().serialize(encoder, value)
is TelegramMediaSticker -> TelegramMediaSticker.serializer().serialize(encoder, value)

View File

@@ -19,6 +19,7 @@ object MediaGroupMemberTelegramMediaSerializer : KSerializer<MediaGroupMemberTel
when (value) {
is TelegramMediaPhoto -> TelegramMediaPhoto.serializer().serialize(encoder, value)
is TelegramMediaVideo -> TelegramMediaVideo.serializer().serialize(encoder, value)
is TelegramMediaLivePhoto -> TelegramMediaLivePhoto.serializer().serialize(encoder, value)
is TelegramMediaAudio -> TelegramMediaAudio.serializer().serialize(encoder, value)
is TelegramMediaDocument -> TelegramMediaDocument.serializer().serialize(encoder, value)
}
@@ -30,6 +31,7 @@ object MediaGroupMemberTelegramMediaSerializer : KSerializer<MediaGroupMemberTel
return when (json[typeField] ?.jsonPrimitive ?.contentOrNull) {
TelegramMediaPhoto.TYPE -> nonstrictJsonFormat.decodeFromJsonElement(TelegramMediaPhoto.serializer(), json)
TelegramMediaVideo.TYPE -> nonstrictJsonFormat.decodeFromJsonElement(TelegramMediaVideo.serializer(), json)
TelegramMediaLivePhoto.TYPE -> nonstrictJsonFormat.decodeFromJsonElement(TelegramMediaLivePhoto.serializer(), json)
TelegramMediaAudio.TYPE -> nonstrictJsonFormat.decodeFromJsonElement(TelegramMediaAudio.serializer(), json)
TelegramMediaDocument.TYPE -> nonstrictJsonFormat.decodeFromJsonElement(TelegramMediaDocument.serializer(), json)
else -> error("Illegal type of incoming MediaGroupMemberTelegramMedia")

View File

@@ -6,9 +6,11 @@ import dev.inmo.tgbotapi.types.documentField
import dev.inmo.tgbotapi.types.files.AnimationFile
import dev.inmo.tgbotapi.types.files.AudioFile
import dev.inmo.tgbotapi.types.files.DocumentFile
import dev.inmo.tgbotapi.types.files.LivePhotoFile
import dev.inmo.tgbotapi.types.files.PhotoFile
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.files.VideoFile
import dev.inmo.tgbotapi.types.livePhotoField
import dev.inmo.tgbotapi.types.location.StaticLocation
import dev.inmo.tgbotapi.types.locationField
import dev.inmo.tgbotapi.types.photoField
@@ -34,6 +36,8 @@ interface PollMedia : BaseTelegramMediaFile {
val audio: AudioFile? = null,
@SerialName(documentField)
val document: DocumentFile? = null,
@SerialName(livePhotoField)
val livePhoto: LivePhotoFile? = null,
@SerialName(photoField)
val photo: PhotoFile? = null,
@SerialName(stickerField)
@@ -57,6 +61,7 @@ interface PollMedia : BaseTelegramMediaFile {
surrogate.animation != null -> surrogate.animation
surrogate.audio != null -> surrogate.audio
surrogate.document != null -> surrogate.document
surrogate.livePhoto != null -> surrogate.livePhoto
surrogate.photo != null -> surrogate.photo
surrogate.sticker != null -> surrogate.sticker
surrogate.video != null -> surrogate.video
@@ -71,6 +76,7 @@ interface PollMedia : BaseTelegramMediaFile {
animation = value as? AnimationFile,
audio = value as? AudioFile,
document = value as? DocumentFile,
livePhoto = value as? LivePhotoFile,
photo = value as? PhotoFile,
sticker = value as? Sticker,
video = value as? VideoFile,

View File

@@ -0,0 +1,83 @@
package dev.inmo.tgbotapi.types.media
import dev.inmo.tgbotapi.requests.abstracts.InputFile
import dev.inmo.tgbotapi.requests.abstracts.fileIdToSend
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.types.message.parseModeField
import dev.inmo.tgbotapi.types.message.*
import dev.inmo.tgbotapi.types.message.RawMessageEntity
import dev.inmo.tgbotapi.types.message.toRawMessageEntities
import dev.inmo.tgbotapi.utils.extensions.makeString
import kotlinx.serialization.*
fun TelegramMediaLivePhoto(
file: InputFile,
photo: InputFile,
text: String? = null,
parseMode: ParseMode? = null,
spoilered: Boolean = false,
showCaptionAboveMedia: Boolean = false
) = TelegramMediaLivePhoto(
file = file,
photo = photo,
text = text,
parseMode = parseMode,
rawEntities = null,
spoilered = spoilered,
showCaptionAboveMedia = showCaptionAboveMedia
)
fun TelegramMediaLivePhoto(
file: InputFile,
photo: InputFile,
entities: TextSourcesList,
spoilered: Boolean = false,
showCaptionAboveMedia: Boolean = false
) = TelegramMediaLivePhoto(
file = file,
photo = photo,
text = entities.makeString(),
parseMode = null,
rawEntities = entities.toRawMessageEntities(),
spoilered = spoilered,
showCaptionAboveMedia = showCaptionAboveMedia
)
@ConsistentCopyVisibility
@Serializable
data class TelegramMediaLivePhoto internal constructor(
override val file: InputFile,
@SerialName(photoField)
val photo: InputFile,
@SerialName(captionField)
override val text: String? = null,
@SerialName(parseModeField)
override val parseMode: ParseMode? = null,
@SerialName(captionEntitiesField)
private val rawEntities: List<RawMessageEntity>? = null,
@SerialName(hasSpoilerField)
override val spoilered: Boolean = false,
@SerialName(showCaptionAboveMediaField)
override val showCaptionAboveMedia: Boolean = false,
) : TelegramFreeMedia,
VisualMediaGroupMemberTelegramMedia,
InputPollMedia,
InputPollOptionMedia {
@EncodeDefault
override val type: String = TYPE
override val textSources: TextSourcesList? by lazy {
rawEntities ?.asTextSources(text ?: return@lazy null)
}
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
@SerialName(mediaField)
override val media: String
init { media = file.fileIdToSend } // crutch until js compiling will be fixed
companion object {
const val TYPE = "live_photo"
}
}

View File

@@ -18,10 +18,12 @@ object TelegramMediaSerializer : KSerializer<TelegramMedia> {
is TelegramMediaVideo -> TelegramMediaVideo.serializer().serialize(encoder, value)
is TelegramMediaAudio -> TelegramMediaAudio.serializer().serialize(encoder, value)
is TelegramMediaPhoto -> TelegramMediaPhoto.serializer().serialize(encoder, value)
is TelegramMediaLivePhoto -> TelegramMediaLivePhoto.serializer().serialize(encoder, value)
is TelegramMediaAnimation -> TelegramMediaAnimation.serializer().serialize(encoder, value)
is TelegramMediaDocument -> TelegramMediaDocument.serializer().serialize(encoder, value)
is TelegramPaidMediaVideo -> TelegramPaidMediaVideo.serializer().serialize(encoder, value)
is TelegramPaidMediaPhoto -> TelegramPaidMediaPhoto.serializer().serialize(encoder, value)
is TelegramPaidMediaLivePhoto -> TelegramPaidMediaLivePhoto.serializer().serialize(encoder, value)
is TelegramMediaSticker -> TelegramMediaSticker.serializer().serialize(encoder, value)
}
}

View File

@@ -0,0 +1,28 @@
package dev.inmo.tgbotapi.types.media
import dev.inmo.tgbotapi.requests.abstracts.InputFile
import dev.inmo.tgbotapi.requests.abstracts.fileIdToSend
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.files.LivePhotoFile
import dev.inmo.tgbotapi.types.files.toTelegramPaidMediaLivePhoto
import dev.inmo.tgbotapi.types.message.payments.PaidMedia
import kotlinx.serialization.*
@Serializable
data class TelegramPaidMediaLivePhoto(
override val file: InputFile,
@SerialName(photoField)
val photo: InputFile,
) : VisualTelegramPaidMedia {
override val type: String = TYPE
@SerialName(mediaField)
override val media: String
init { media = file.fileIdToSend } // crutch until js compiling will be fixed
companion object {
const val TYPE = "live_photo"
}
}
fun PaidMedia.LivePhoto.toTelegramPaidMediaLivePhoto(): TelegramPaidMediaLivePhoto = livePhoto.toTelegramPaidMediaLivePhoto()

View File

@@ -102,6 +102,7 @@ internal data class RawMessage(
private val photo: PhotoFile? = null,
private val sticker: Sticker? = null,
private val video: VideoFile? = null,
private val live_photo: LivePhotoFile? = null,
private val voice: VoiceFile? = null,
private val video_note: VideoNoteFile? = null,
private val contact: Contact? = null,
@@ -243,6 +244,14 @@ internal data class RawMessage(
quote,
show_caption_above_media
)
live_photo != null -> LivePhotoContent(
live_photo,
caption,
adaptedCaptionEntities,
has_media_spoiler ?: false,
quote,
show_caption_above_media
)
animation != null -> AnimationContent(
animation,
document,

View File

@@ -43,6 +43,7 @@ sealed interface MessageContent: ResendableContent {
subclass(PhotoContent::class)
subclass(VideoContent::class)
subclass(LivePhotoContent::class)
subclass(AudioContent::class)
subclass(DocumentContent::class)
@@ -65,6 +66,7 @@ sealed interface MessageContent: ResendableContent {
subclass(AudioContent::class)
subclass(DocumentContent::class)
subclass(VideoContent::class)
subclass(LivePhotoContent::class)
subclass(PhotoContent::class)
subclass(AnimationContent::class)
}
@@ -77,6 +79,7 @@ sealed interface MessageContent: ResendableContent {
polymorphic(MediaContent::class) {
subclass(VideoNoteContent::class)
subclass(VideoContent::class)
subclass(LivePhotoContent::class)
subclass(StickerContent::class)
subclass(PhotoContent::class)
subclass(VoiceContent::class)
@@ -88,6 +91,7 @@ sealed interface MessageContent: ResendableContent {
}
polymorphic(SpoilerableMediaContent::class) {
subclass(VideoContent::class)
subclass(LivePhotoContent::class)
subclass(PhotoContent::class)
subclass(AnimationContent::class)
@@ -122,6 +126,7 @@ sealed interface MessageContent: ResendableContent {
polymorphic(VisualMediaGroupPartContent::class) {
subclass(PhotoContent::class)
subclass(VideoContent::class)
subclass(LivePhotoContent::class)
visualMediaGroupContentAdditionalBuilder()
}

View File

@@ -0,0 +1,63 @@
package dev.inmo.tgbotapi.types.message.content
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.media.SendLivePhoto
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
import dev.inmo.tgbotapi.types.media.TelegramMediaLivePhoto
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.LivePhotoFile
import dev.inmo.tgbotapi.types.files.toTelegramMediaLivePhoto
import dev.inmo.tgbotapi.types.message.SuggestedPostParameters
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import kotlinx.serialization.Serializable
@Serializable
data class LivePhotoContent(
override val media: LivePhotoFile,
override val text: String? = null,
override val textSources: TextSourcesList = emptyList(),
override val spoilered: Boolean = false,
override val quote: TextQuote? = null,
override val showCaptionAboveMedia: Boolean = false
) : VisualMediaGroupPartContent {
override fun createResend(
chatId: ChatIdentifier,
messageThreadId: MessageThreadId?,
directMessageThreadId: DirectMessageThreadId?,
businessConnectionId: BusinessConnectionId?,
disableNotification: Boolean,
protectContent: Boolean,
allowPaidBroadcast: Boolean,
effectId: EffectId?,
suggestedPostParameters: SuggestedPostParameters?,
replyParameters: ReplyParameters?,
replyMarkup: KeyboardMarkup?
): Request<ContentMessage<LivePhotoContent>> = SendLivePhoto(
chatId = chatId,
livePhoto = media.fileId,
photo = media.photo ?.fileId ?: media.fileId,
entities = textSources,
showCaptionAboveMedia = showCaptionAboveMedia,
spoilered = spoilered,
threadId = messageThreadId,
directMessageThreadId = directMessageThreadId,
businessConnectionId = businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
allowPaidBroadcast = allowPaidBroadcast,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)
override fun toMediaGroupMemberTelegramMedia(): TelegramMediaLivePhoto = asTelegramMedia()
override fun asTelegramMedia(): TelegramMediaLivePhoto = media.toTelegramMediaLivePhoto(
textSources = textSources,
spoilered = spoilered,
showCaptionAboveMedia = showCaptionAboveMedia
)
}

View File

@@ -45,6 +45,7 @@ data class PaidMediaInfoContent(
is PaidMedia.Preview -> null
is PaidMedia.Unknown -> null
is PaidMedia.Video -> it.video.toTelegramPaidMediaVideo()
is PaidMedia.LivePhoto -> it.livePhoto.toTelegramPaidMediaLivePhoto()
}
}.ifEmpty {
error("Unable to create resend for paid media content without any revealed content")

View File

@@ -30,6 +30,7 @@ typealias DocumentMediaGroupMessage = CommonMessage<DocumentMediaGroupPartConten
typealias DocumentMessage = CommonMessage<DocumentContent>
typealias VisualMediaGroupMessage = CommonMessage<VisualMediaGroupPartContent>
typealias VideoMessage = CommonMessage<VideoContent>
typealias LivePhotoMessage = CommonMessage<LivePhotoContent>
typealias PhotoMessage = CommonMessage<PhotoContent>
typealias AnimationMessage = CommonMessage<AnimationContent>
typealias ScheduledGiveawayContentMessage = CommonMessage<GiveawayContent>

View File

@@ -4,6 +4,7 @@
package dev.inmo.tgbotapi.types.message.payments
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.files.LivePhotoFile
import dev.inmo.tgbotapi.types.files.PhotoFile
import dev.inmo.tgbotapi.types.files.TelegramMediaFile
import dev.inmo.tgbotapi.types.files.VideoFile
@@ -68,6 +69,20 @@ sealed interface PaidMedia {
}
}
@Serializable
data class LivePhoto(
@SerialName(livePhotoField)
val livePhoto: LivePhotoFile
) : PaidMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = Companion.type
companion object {
val type: String = "live_photo"
}
}
@Serializable(Companion::class)
data class Unknown(
@SerialName(typeField)
@@ -89,7 +104,9 @@ sealed interface PaidMedia {
@SerialName(photoField)
val photo: PhotoFile? = null,
@SerialName(videoField)
val video: VideoFile? = null
val video: VideoFile? = null,
@SerialName(livePhotoField)
val livePhoto: LivePhotoFile? = null
)
override val descriptor: SerialDescriptor
@@ -112,6 +129,9 @@ sealed interface PaidMedia {
Video.type -> Video(
data.video ?: return unknown
)
LivePhoto.type -> LivePhoto(
data.livePhoto ?: return unknown
)
else -> unknown
}
}
@@ -127,6 +147,7 @@ sealed interface PaidMedia {
(value as? Preview) ?.duration,
(value as? Photo) ?.photo,
(value as? Video) ?.video,
(value as? LivePhoto) ?.livePhoto,
)
Surrogate.serializer().serialize(encoder, surrogate)
}

View File

@@ -5,6 +5,7 @@ import dev.inmo.tgbotapi.types.media.*
fun PaidMedia.toTelegramPaidMediaOrNull(): TelegramPaidMedia? = when (this) {
is PaidMedia.Photo -> toTelegramMediaPhoto()
is PaidMedia.Video -> toTelegramPaidMediaVideo()
is PaidMedia.LivePhoto -> toTelegramPaidMediaLivePhoto()
is PaidMedia.Preview, is PaidMedia.Unknown -> null
}