rework of multipart files with fixes in new sticker sets

This commit is contained in:
InsanusMokrassar 2023-03-11 21:17:59 +06:00
parent 6722ab5f50
commit e5a48e9684
18 changed files with 295 additions and 139 deletions

View File

@ -45,6 +45,7 @@ microutils-coroutines = { module = "dev.inmo:micro_utils.coroutines", version.re
microutils-serialization-base64 = { module = "dev.inmo:micro_utils.serialization.base64", version.ref = "microutils" }
microutils-serialization-encapsulator = { module = "dev.inmo:micro_utils.serialization.encapsulator", version.ref = "microutils" }
microutils-serialization-typedSerializer = { module = "dev.inmo:micro_utils.serialization.typed_serializer", version.ref = "microutils" }
microutils-serialization-mapper = { module = "dev.inmo:micro_utils.serialization.mapper", version.ref = "microutils" }
microutils-languageCodes = { module = "dev.inmo:micro_utils.language_codes", version.ref = "microutils" }
microutils-ktor-common = { module = "dev.inmo:micro_utils.ktor.common", version.ref = "microutils" }
microutils-fsm-common = { module = "dev.inmo:micro_utils.fsm.common", version.ref = "microutils" }

View File

@ -9,6 +9,7 @@ import dev.inmo.tgbotapi.types.files.TelegramMediaFile
import dev.inmo.tgbotapi.types.message.content.MediaContent
import io.ktor.util.cio.use
import io.ktor.util.cio.writeChannel
import io.ktor.utils.io.copyAndClose
import io.ktor.utils.io.copyTo
import kotlinx.coroutines.job
import java.io.File
@ -25,7 +26,7 @@ suspend fun TelegramBot.downloadFile(
doOutsideOfCoroutine { destFile.createNewFile() }
destFile.writeChannel(coroutineContext.job).use {
readChannel.copyTo(this)
readChannel.copyAndClose(this)
}
return destFile

View File

@ -30,11 +30,17 @@ suspend fun TelegramBot.downloadFileToTemp(
suspend fun TelegramBot.downloadFileToTemp(
pathedFile: PathedFile
) = downloadFileToTemp(
): File = downloadFileToTemp(
pathedFile.filePath
).apply {
runCatching {
renameTo(File(parentFile, "$nameWithoutExtension.${pathedFile.fileName.fileExtension}"))
).run {
val newFile = File(parentFile, "$nameWithoutExtension.${pathedFile.fileName.fileExtension}")
val success = runCatching {
renameTo(newFile)
}.getOrElse { false }
if (success) {
newFile
} else {
this@run
}
}

View File

@ -26,6 +26,7 @@ kotlin {
api libs.microutils.serialization.base64
api libs.microutils.serialization.encapsulator
api libs.microutils.serialization.typedSerializer
api libs.microutils.serialization.mapper
api libs.microutils.ktor.common
api libs.microutils.languageCodes

View File

@ -36,8 +36,10 @@ sealed class InputFile {
}
}
internal const val attachPrefix = "attach://"
internal inline val InputFile.attachFileId
get() = "attach://$fileId"
get() = "$attachPrefix$fileId"
internal inline val InputFile.fileIdToSend
get() = when (this) {
is FileId -> fileId
@ -60,8 +62,8 @@ fun String.toInputFile() = FileId(this)
@RiskFeature
object InputFileSerializer : KSerializer<InputFile> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(FileId::class.toString(), PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: InputFile) = encoder.encodeString(value.fileId)
override fun deserialize(decoder: Decoder): FileId = FileId(decoder.decodeString())
override fun serialize(encoder: Encoder, value: InputFile) = encoder.encodeString(value.fileIdToSend)
override fun deserialize(decoder: Decoder): FileId = FileId(decoder.decodeString().removePrefix(attachPrefix))
}
// TODO:: add checks for files size

View File

@ -1,6 +1,7 @@
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.*
@ -22,7 +23,7 @@ import kotlinx.serialization.*
fun SendAnimation(
chatId: ChatIdentifier,
animation: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
text: String? = null,
parseMode: ParseMode? = null,
spoilered: Boolean = false,
@ -36,15 +37,13 @@ fun SendAnimation(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<AnimationContent>> {
val animationAsFileId = (animation as? FileId) ?.fileId
val animationAsFile = animation as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendAnimationData(
chatId,
animationAsFileId,
thumbAsFileId,
animation,
thumbnail ?.fileId,
text,
parseMode,
null,
@ -63,9 +62,9 @@ fun SendAnimation(
return if (animationAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendAnimationFiles(animationAsFile, thumbAsFile)
listOfNotNull(animationAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -73,7 +72,7 @@ fun SendAnimation(
fun SendAnimation(
chatId: ChatIdentifier,
animation: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
entities: TextSourcesList,
spoilered: Boolean = false,
duration: Long? = null,
@ -86,15 +85,13 @@ fun SendAnimation(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<AnimationContent>> {
val animationAsFileId = (animation as? FileId) ?.fileId
val animationAsFile = animation as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendAnimationData(
chatId,
animationAsFileId,
thumbAsFileId,
animation,
thumbnail ?.fileId,
entities.makeString(),
null,
entities.toRawMessageEntities(),
@ -113,9 +110,9 @@ fun SendAnimation(
return if (animationAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendAnimationFiles(animationAsFile, thumbAsFile)
listOfNotNull(animationAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -128,7 +125,7 @@ data class SendAnimationData internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(animationField)
val animation: String? = null,
val animation: InputFile,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(captionField)

View File

@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.requests.send.media
import dev.inmo.tgbotapi.abstracts.Performerable
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.*
@ -23,7 +24,7 @@ import kotlinx.serialization.*
fun SendAudio(
chatId: ChatIdentifier,
audio: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
text: String? = null,
parseMode: ParseMode? = null,
duration: Long? = null,
@ -36,15 +37,13 @@ fun SendAudio(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<AudioContent>> {
val audioAsFileId = (audio as? FileId) ?.fileId
val audioAsFile = audio as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendAudioData(
chatId,
audioAsFileId,
thumbAsFileId,
audio,
thumbnail ?.fileId,
text,
parseMode,
null,
@ -62,9 +61,9 @@ fun SendAudio(
return if (audioAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendAudioFiles(audioAsFile, thumbAsFile)
listOfNotNull(audioAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -72,7 +71,7 @@ fun SendAudio(
fun SendAudio(
chatId: ChatIdentifier,
audio: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
entities: List<TextSource>,
duration: Long? = null,
performer: String? = null,
@ -84,15 +83,13 @@ fun SendAudio(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<AudioContent>> {
val audioAsFileId = (audio as? FileId) ?.fileId
val audioAsFile = audio as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendAudioData(
chatId,
audioAsFileId,
thumbAsFileId,
audio,
thumbnail ?.fileId,
entities.makeString(),
null,
entities.toRawMessageEntities(),
@ -110,9 +107,9 @@ fun SendAudio(
return if (audioAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendAudioFiles(audioAsFile, thumbAsFile)
listOfNotNull(audioAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -125,7 +122,7 @@ data class SendAudioData internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(audioField)
val audio: String? = null,
val audio: InputFile,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(captionField)

View File

@ -1,6 +1,7 @@
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.*
@ -31,7 +32,7 @@ import kotlinx.serialization.*
fun SendDocument(
chatId: ChatIdentifier,
document: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
text: String? = null,
parseMode: ParseMode? = null,
threadId: MessageThreadId? = chatId.threadId,
@ -42,15 +43,13 @@ fun SendDocument(
replyMarkup: KeyboardMarkup? = null,
disableContentTypeDetection: Boolean? = null
): Request<ContentMessage<DocumentContent>> {
val documentAsFileId = (document as? FileId) ?.fileId
val documentAsFile = document as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendDocumentData(
chatId,
documentAsFileId,
thumbAsFileId,
document,
thumbnail ?.fileId,
text,
parseMode,
null,
@ -66,9 +65,9 @@ fun SendDocument(
return if (documentAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendDocumentFiles(documentAsFile, thumbAsFile)
listOfNotNull(documentAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -85,7 +84,7 @@ fun SendDocument(
fun SendDocument(
chatId: ChatIdentifier,
document: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
entities: TextSourcesList,
threadId: MessageThreadId? = chatId.threadId,
disableNotification: Boolean = false,
@ -95,15 +94,13 @@ fun SendDocument(
replyMarkup: KeyboardMarkup? = null,
disableContentTypeDetection: Boolean? = null
): Request<ContentMessage<DocumentContent>> {
val documentAsFileId = (document as? FileId) ?.fileId
val documentAsFile = document as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendDocumentData(
chatId,
documentAsFileId,
thumbAsFileId,
document,
thumbnail ?.fileId,
entities.makeString(),
null,
entities.toRawMessageEntities(),
@ -119,9 +116,9 @@ fun SendDocument(
return if (documentAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendDocumentFiles(documentAsFile, thumbAsFile)
listOfNotNull(documentAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -143,7 +140,7 @@ data class SendDocumentData internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(documentField)
val document: String? = null,
val document: InputFile,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(captionField)

View File

@ -2,10 +2,12 @@ package dev.inmo.tgbotapi.requests.send.media
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.send.abstracts.SendMessageRequest
import dev.inmo.tgbotapi.requests.send.media.base.*
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.media.*
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.PossiblySentViaBotCommonMessage
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializerClass
import dev.inmo.tgbotapi.types.message.content.MediaGroupPartContent
@ -37,7 +39,7 @@ fun <T : MediaGroupPartContent> SendMediaGroup(
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null
): Request<PossiblySentViaBotCommonMessage<MediaGroupContent<T>>> {
): Request<ContentMessage<MediaGroupContent<T>>> {
if (media.size !in mediaCountInMediaGroup) {
throwRangeError("Count of members in media group", mediaCountInMediaGroup, media.size)
}
@ -66,11 +68,11 @@ fun <T : MediaGroupPartContent> SendMediaGroup(
return (if (files.isEmpty()) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendMediaGroupFiles(files)
files.associateBy { it.fileId }
)
}) as Request<PossiblySentViaBotCommonMessage<MediaGroupContent<T>>>
}) as Request<ContentMessage<MediaGroupContent<T>>>
}
/**

View File

@ -1,6 +1,7 @@
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.*
@ -33,7 +34,7 @@ fun SendPhoto(
): Request<ContentMessage<PhotoContent>> {
val data = SendPhotoData(
chatId,
(photo as? FileId) ?.fileId,
photo,
text,
parseMode,
null,
@ -45,12 +46,14 @@ fun SendPhoto(
allowSendingWithoutReply,
replyMarkup
)
return data.photo ?.let {
return if (photo is MultipartFile) {
CommonMultipartFileRequest(
data,
listOf(photo).associateBy { it.fileId }
)
} else {
data
} ?: MultipartRequestImpl(
data,
SendPhotoFiles(photo as MultipartFile)
)
}
}
fun SendPhoto(
@ -67,7 +70,7 @@ fun SendPhoto(
): Request<ContentMessage<PhotoContent>> {
val data = SendPhotoData(
chatId,
(photo as? FileId)?.fileId,
photo,
entities.makeString(),
null,
entities.toRawMessageEntities(),
@ -79,12 +82,15 @@ fun SendPhoto(
allowSendingWithoutReply,
replyMarkup
)
return data.photo ?.let {
return if (photo is MultipartFile) {
CommonMultipartFileRequest(
data,
listOf(photo).associateBy { it.fileId }
)
} else {
data
} ?: MultipartRequestImpl(
data,
SendPhotoFiles(photo as MultipartFile)
)
}
}
private val commonResultDeserializer: DeserializationStrategy<ContentMessage<PhotoContent>>
@ -95,7 +101,7 @@ data class SendPhotoData internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(photoField)
val photo: String? = null,
val photo: InputFile,
@SerialName(captionField)
override val text: String? = null,
@SerialName(parseModeField)

View File

@ -1,6 +1,7 @@
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.ReplyingMarkupSendMessageRequest
import dev.inmo.tgbotapi.requests.send.abstracts.SendMessageRequest
import dev.inmo.tgbotapi.types.*
@ -28,7 +29,7 @@ fun SendSticker(
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<StickerContent>> = SendStickerByFileId(
chatId,
sticker as? FileId,
sticker,
threadId,
disableNotification,
protectContent,
@ -37,7 +38,10 @@ fun SendSticker(
replyMarkup
).let {
when (sticker) {
is MultipartFile -> SendStickerByFile(it, sticker, emoji)
is MultipartFile -> CommonMultipartFileRequest(
it,
listOf(sticker).associateBy { it.fileId }
)
is FileId -> it
}
}
@ -50,7 +54,7 @@ data class SendStickerByFileId internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(stickerField)
val sticker: FileId? = null,
val sticker: InputFile,
@SerialName(messageThreadIdField)
override val threadId: MessageThreadId? = chatId.threadId,
@SerialName(disableNotificationField)
@ -70,20 +74,3 @@ data class SendStickerByFileId internal constructor(
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}
data class SendStickerByFile internal constructor(
@Transient
private val sendStickerByFileId: SendStickerByFileId,
val sticker: MultipartFile,
val emoji: String?
) : MultipartRequest<ContentMessage<StickerContent>>, Request<ContentMessage<StickerContent>> by sendStickerByFileId {
override val mediaMap: Map<String, MultipartFile> = mapOf(stickerField to sticker)
override val paramsJson: JsonObject
get() {
return JsonObject(
mapOfNotNull(
emojiField to emoji ?.let { JsonPrimitive(it) }
) + sendStickerByFileId.toJsonWithoutNulls(SendStickerByFileId.serializer())
)
}
}

View File

@ -1,6 +1,7 @@
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.*
@ -22,7 +23,7 @@ import kotlinx.serialization.*
fun SendVideo(
chatId: ChatIdentifier,
video: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
text: String? = null,
parseMode: ParseMode? = null,
spoilered: Boolean = false,
@ -37,15 +38,13 @@ fun SendVideo(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<VideoContent>> {
val videoAsFileId = (video as? FileId) ?.fileId
val videoAsFile = video as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendVideoData(
chatId,
videoAsFileId,
thumbAsFileId,
video,
thumbnail ?.fileId,
text,
parseMode,
null,
@ -65,9 +64,9 @@ fun SendVideo(
return if (videoAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendVideoFiles(videoAsFile, thumbAsFile)
listOfNotNull(videoAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -75,7 +74,7 @@ fun SendVideo(
fun SendVideo(
chatId: ChatIdentifier,
video: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
entities: TextSourcesList,
spoilered: Boolean = false,
duration: Long? = null,
@ -89,15 +88,13 @@ fun SendVideo(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<VideoContent>> {
val videoAsFileId = (video as? FileId) ?.fileId
val videoAsFile = video as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendVideoData(
chatId,
videoAsFileId,
thumbAsFileId,
video,
thumbnail ?.fileId,
entities.makeString(),
null,
entities.toRawMessageEntities(),
@ -117,9 +114,9 @@ fun SendVideo(
return if (videoAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendVideoFiles(videoAsFile, thumbAsFile)
listOfNotNull(videoAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -132,7 +129,7 @@ data class SendVideoData internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(videoField)
val video: String? = null,
val video: InputFile,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(captionField)

View File

@ -1,6 +1,7 @@
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.*
@ -14,7 +15,7 @@ import kotlinx.serialization.*
fun SendVideoNote(
chatId: ChatIdentifier,
videoNote: InputFile,
thumb: InputFile? = null,
thumbnail: InputFile? = null,
duration: Long? = null,
size: Int? = null, // in documentation - length (size of video side)
threadId: MessageThreadId? = chatId.threadId,
@ -24,15 +25,13 @@ fun SendVideoNote(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<VideoNoteContent>> {
val videoNoteAsFileId = (videoNote as? FileId) ?.fileId
val videoNoteAsFile = videoNote as? MultipartFile
val thumbAsFileId = (thumb as? FileId) ?.fileId
val thumbAsFile = thumb as? MultipartFile
val thumbAsFile = thumbnail as? MultipartFile
val data = SendVideoNoteData(
chatId,
videoNoteAsFileId,
thumbAsFileId,
videoNote,
thumbnail ?.fileId,
duration,
size,
threadId,
@ -46,9 +45,9 @@ fun SendVideoNote(
return if (videoNoteAsFile == null && thumbAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendVideoNoteFiles(videoNoteAsFile, thumbAsFile)
listOfNotNull(videoNoteAsFile, thumbAsFile).associateBy { it.fileId }
)
}
}
@ -61,7 +60,7 @@ data class SendVideoNoteData internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(videoNoteField)
val videoNote: String? = null,
val videoNote: InputFile,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(durationField)

View File

@ -1,6 +1,7 @@
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.*
@ -32,12 +33,11 @@ fun SendVoice(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<VoiceContent>> {
val voiceAsFileId = (voice as? FileId) ?.fileId
val voiceAsFile = voice as? MultipartFile
val data = SendVoiceData(
chatId,
voiceAsFileId,
voice,
text,
parseMode,
null,
@ -53,9 +53,9 @@ fun SendVoice(
return if (voiceAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendVoiceFiles(voiceAsFile)
listOfNotNull(voiceAsFile).associateBy { it.fileId }
)
}
}
@ -72,12 +72,11 @@ fun SendVoice(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
): Request<ContentMessage<VoiceContent>> {
val voiceAsFileId = (voice as? FileId) ?.fileId
val voiceAsFile = voice as? MultipartFile
val data = SendVoiceData(
chatId,
voiceAsFileId,
voice,
entities.makeString(),
null,
entities.toRawMessageEntities(),
@ -93,9 +92,9 @@ fun SendVoice(
return if (voiceAsFile == null) {
data
} else {
MultipartRequestImpl(
CommonMultipartFileRequest(
data,
SendVoiceFiles(voiceAsFile)
listOfNotNull(voiceAsFile).associateBy { it.fileId }
)
}
}
@ -108,7 +107,7 @@ data class SendVoiceData internal constructor(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(voiceField)
val voice: String? = null,
val voice: InputFile,
@SerialName(captionField)
override val text: String? = null,
@SerialName(parseModeField)

View File

@ -1,11 +1,15 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.micro_utils.serialization.mapper.MapperSerializer
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.CreateStickerSetAction
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
* Will create one of [CreateNewStickerSet] types based on the first element of [stickers]
@ -26,18 +30,30 @@ fun CreateNewStickerSet(
is InputSticker.WithKeywords.CustomEmoji -> CreateNewStickerSet.CustomEmoji(userId, name, title, stickersFormat, stickers.filterIsInstance<InputSticker.WithKeywords.CustomEmoji>(), needsRepainting)
is InputSticker.WithKeywords.Regular -> CreateNewStickerSet.Regular(userId, name, title, stickersFormat, stickers.filterIsInstance<InputSticker.WithKeywords.Regular>())
}
val multipartParts = stickers.mapNotNull { (it.sticker as? MultipartFile) }
val multipartParts = stickers.mapNotNull {
(it.sticker as? MultipartFile)
}
return if (multipartParts.isNotEmpty()) {
CommonMultipartFileRequest(
data,
multipartParts.associateBy { it.fileId }
)
when (data) { // cratch for exact determining of common multipart data type
is CreateNewStickerSet.CustomEmoji -> CommonMultipartFileRequest(
data,
multipartParts.associateBy { it.fileId }
)
is CreateNewStickerSet.Mask -> CommonMultipartFileRequest(
data,
multipartParts.associateBy { it.fileId }
)
is CreateNewStickerSet.Regular -> CommonMultipartFileRequest(
data,
multipartParts.associateBy { it.fileId }
)
}
} else {
data
}
}
@Serializable
@Serializable(CreateNewStickerSetSerializer::class)
sealed interface CreateNewStickerSet : CreateStickerSetAction {
val stickerType: StickerType
val stickers: List<InputSticker>
@ -101,4 +117,70 @@ sealed interface CreateNewStickerSet : CreateStickerSetAction {
override val stickerType: StickerType
get() = StickerType.CustomEmoji
}
@Serializable
data class SurrogateCreateNewSticker internal constructor(
@SerialName(userIdField)
override val userId: UserId,
@SerialName(nameField)
override val name: String,
@SerialName(titleField)
override val title: String,
@SerialName(stickerFormatField)
val stickersFormat: StickerFormat,
@SerialName(stickersField)
val stickers: List<InputSticker>,
@SerialName(stickerTypeField)
val stickerType: StickerType,
@SerialName(needsRepaintingField)
val needsRepainting: Boolean? = null
) : CreateStickerSetAction {
override val requestSerializer: SerializationStrategy<*>
get() = CreateNewStickerSet.serializer()
override fun method(): String = "createNewStickerSet"
}
}
object CreateNewStickerSetSerializer : KSerializer<CreateNewStickerSet>,
MapperSerializer<CreateNewStickerSet.SurrogateCreateNewSticker, CreateNewStickerSet>(
CreateNewStickerSet.SurrogateCreateNewSticker.serializer(),
{
CreateNewStickerSet.SurrogateCreateNewSticker(
it.userId,
it.name,
it.title,
it.stickersFormat,
it.stickers,
it.stickerType,
(it as? CreateNewStickerSet.CustomEmoji)?.needsRepainting
)
},
{
when (it.stickerType) {
StickerType.CustomEmoji -> CreateNewStickerSet.CustomEmoji(
it.userId,
it.name,
it.title,
it.stickersFormat,
it.stickers.filterIsInstance<InputSticker.WithKeywords.CustomEmoji>(),
it.needsRepainting
)
StickerType.Mask -> CreateNewStickerSet.Mask(
it.userId,
it.name,
it.title,
it.stickersFormat,
it.stickers.filterIsInstance<InputSticker.Mask>(),
)
StickerType.Regular -> CreateNewStickerSet.Regular(
it.userId,
it.name,
it.title,
it.stickersFormat,
it.stickers.filterIsInstance<InputSticker.WithKeywords.Regular>(),
)
is StickerType.Unknown -> error("Unable to create new sticker set due to error in type format: ${it.stickerType}")
}
}
)

View File

@ -1,17 +1,20 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.micro_utils.serialization.mapper.MapperSerializer
import dev.inmo.tgbotapi.requests.abstracts.InputFile
import dev.inmo.tgbotapi.types.StickerType
import dev.inmo.tgbotapi.types.emojiListField
import dev.inmo.tgbotapi.types.keywordsField
import dev.inmo.tgbotapi.types.maskPositionField
import dev.inmo.tgbotapi.types.stickerField
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ClassCastsIncluded
@Serializable
@Serializable(InputStickerSerializer::class)
sealed interface InputSticker {
val sticker: InputFile
val emojisList: List<String>
@ -51,3 +54,69 @@ sealed interface InputSticker {
) : WithKeywords
}
}
object InputStickerSerializer : KSerializer<InputSticker>, MapperSerializer<InputStickerSerializer.SurrogateInputSticker, InputSticker>(
SurrogateInputSticker.serializer(),
{
when (it) {
is InputSticker.Mask -> SurrogateInputSticker(
it.sticker,
it.emojisList,
emptyList(),
it.maskPosition,
StickerType.Mask
)
is InputSticker.WithKeywords.CustomEmoji -> SurrogateInputSticker(
it.sticker,
it.emojisList,
it.keywords,
null,
StickerType.CustomEmoji
)
is InputSticker.WithKeywords.Regular -> SurrogateInputSticker(
it.sticker,
it.emojisList,
it.keywords,
null,
StickerType.Regular
)
}
},
{
when (it.internalType) {
StickerType.CustomEmoji -> InputSticker.WithKeywords.CustomEmoji(
it.sticker,
it.emojisList,
it.keywords
)
StickerType.Mask -> InputSticker.Mask(
it.sticker,
it.emojisList,
it.maskPosition
)
StickerType.Regular -> InputSticker.WithKeywords.Regular(
it.sticker,
it.emojisList,
it.keywords
)
is StickerType.Unknown -> InputSticker.WithKeywords.Regular(
it.sticker,
it.emojisList,
it.keywords
)
}
},
) {
@Serializable
data class SurrogateInputSticker internal constructor(
@SerialName(stickerField)
val sticker: InputFile,
@SerialName(emojiListField)
val emojisList: List<String>,
@SerialName(keywordsField)
val keywords: List<String> = emptyList(),
@SerialName(maskPositionField)
val maskPosition: MaskPosition? = null,
internal val internalType: StickerType = StickerType.Unknown()
)
}

View File

@ -118,6 +118,18 @@ sealed interface MediaCollectionContent<T: TelegramMediaFile>: MessageContent, M
sealed interface MediaContent: MessageContent {
val media: TelegramMediaFile
fun asTelegramMedia(): TelegramMedia
override fun createResend(
chatId: ChatIdentifier,
messageThreadId: MessageThreadId?,
disableNotification: Boolean,
protectContent: Boolean,
replyToMessageId: MessageId?,
allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup?
): Request<out ContentMessage<MediaContent>> {
TODO("Not yet implemented")
}
}
sealed interface SpoilerableMediaContent : MediaContent, SpoilerableData

View File

@ -9,6 +9,7 @@ import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.TelegramMediaFile
import dev.inmo.tgbotapi.types.media.TelegramMedia
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.message.textsources.TextSource
import kotlinx.serialization.Serializable
@ -38,7 +39,7 @@ data class MediaGroupContent<T : MediaGroupPartContent>(
replyToMessageId: MessageId?,
allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup?
): Request<out Message> = SendMediaGroup<MediaGroupPartContent>(
): Request<ContentMessage<MediaGroupContent<MediaGroupPartContent>>> = SendMediaGroup<MediaGroupPartContent>(
chatId,
group.map { it.content.toMediaGroupMemberTelegramMedia() },
threadId,