add ExternalReplyInfo

This commit is contained in:
InsanusMokrassar 2024-01-07 19:04:23 +06:00
parent f760e60089
commit cad0bf8a95
25 changed files with 251 additions and 40 deletions

View File

@ -491,6 +491,7 @@ const val isBigField = "is_big"
const val oldReactionField = "old_reaction"
const val newReactionField = "new_reaction"
const val chatField = "chat"
const val originField = "origin"
const val chatsField = "chats"
const val usernameField = "username"
const val bioField = "bio"

View File

@ -16,4 +16,4 @@ data class Contact(
val userId: UserId? = null,
@SerialName(vcardField)
override val vcard: String? = null
) : CommonContactData
) : CommonContactData, ExternalReplyInfo.ContentVariant

View File

@ -0,0 +1,166 @@
package dev.inmo.tgbotapi.types
import dev.inmo.tgbotapi.abstracts.SpoilerableData
import dev.inmo.tgbotapi.types.chat.SuperPublicChat
import dev.inmo.tgbotapi.types.dice.Dice
import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.games.Game
import dev.inmo.tgbotapi.types.games.RawGame
import dev.inmo.tgbotapi.types.giveaway.GiveawayPublicResults
import dev.inmo.tgbotapi.types.giveaway.ScheduledGiveaway
import dev.inmo.tgbotapi.types.location.Location
import dev.inmo.tgbotapi.types.message.MessageOrigin
import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.payments.Invoice
import dev.inmo.tgbotapi.types.polls.Poll
import dev.inmo.tgbotapi.types.stories.Story
import dev.inmo.tgbotapi.types.venue.Venue
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable(ExternalReplyInfo.Companion::class)
@ClassCastsIncluded
sealed interface ExternalReplyInfo {
val origin: MessageOrigin
val chat: SuperPublicChat?
val messageMeta: Message.MetaInfo?
interface ContentVariant
@Serializable
data class Text(
override val origin: MessageOrigin,
override val chat: SuperPublicChat?,
override val messageMeta: Message.MetaInfo?,
val linkPreviewOptions: LinkPreviewOptions?
) : ExternalReplyInfo
@Serializable(ExternalReplyInfo.Companion::class)
sealed interface Content : ExternalReplyInfo {
val content: ContentVariant
@Serializable
data class Simple(
override val origin: MessageOrigin,
override val chat: SuperPublicChat?,
override val messageMeta: Message.MetaInfo?,
override val content: ContentVariant
) : Content
@Serializable
data class Media<T>(
override val origin: MessageOrigin,
override val chat: SuperPublicChat?,
override val messageMeta: Message.MetaInfo?,
override val spoilered: Boolean,
override val content: T
) : Content, SpoilerableData where T: ContentVariant, T : TelegramMediaFile
}
@Serializable
private data class Surrogate(
val origin: MessageOrigin,
val chat: SuperPublicChat? = null,
val message_id: MessageId? = null,
val link_preview_options: LinkPreviewOptions? = null,
val has_media_spoiler: Boolean? = null,
private val story: Story? = null,
private val audio: AudioFile? = null,
private val document: DocumentFile? = null,
private val animation: AnimationFile? = null,
private val game: RawGame? = null,
@Serializable(PhotoSerializer::class)
private val photo: Photo? = null,
private val sticker: Sticker? = null,
private val video: VideoFile? = null,
private val voice: VoiceFile? = null,
private val video_note: VideoNoteFile? = null,
private val contact: Contact? = null,
private val location: Location? = null,
private val venue: Venue? = null,
private val poll: Poll? = null,
private val invoice: Invoice? = null,
private val dice: Dice? = null,
private val giveaway: ScheduledGiveaway? = null,
private val giveaway_winners: GiveawayPublicResults? = null,
) {
val asExternalReplyInfo: ExternalReplyInfo
get() {
val messageMeta = chat ?.let {
message_id ?.let {
Message.MetaInfo(
chat.id,
message_id
)
}
}
val content: ContentVariant? = when {
story != null -> story
audio != null -> audio
video != null -> video
video_note != null -> video_note
animation != null -> animation
document != null -> document
voice != null -> voice
photo != null -> photo
sticker != null -> sticker
dice != null -> dice
game != null -> game.asGame
contact != null -> contact
location != null -> location
venue != null -> venue
poll != null -> poll
invoice != null -> invoice
giveaway != null -> giveaway
giveaway_winners != null -> giveaway_winners
else -> null
}
return content ?.let {
when (it) {
is TelegramMediaFile -> {
Content.Media(
origin,
chat,
messageMeta,
has_media_spoiler == true,
it
)
}
else -> Content.Simple(
origin,
chat,
messageMeta,
it
)
}
} ?: Text(
origin,
chat,
messageMeta,
link_preview_options
)
}
}
@RiskFeature("This serializer currently support only deserialization, but not serialization")
companion object : KSerializer<ExternalReplyInfo> {
override val descriptor: SerialDescriptor
get() = Surrogate.serializer().descriptor
override fun deserialize(decoder: Decoder): ExternalReplyInfo {
return Surrogate.serializer().deserialize(decoder).asExternalReplyInfo
}
override fun serialize(encoder: Encoder, value: ExternalReplyInfo) {
}
}
}

View File

@ -10,4 +10,4 @@ data class Dice(
val value: DiceResult,
@SerialName(emojiField)
val animationType: DiceAnimationType
)
) : ExternalReplyInfo.ContentVariant

View File

@ -23,4 +23,5 @@ data class AnimationFile(
override val mimeType: MimeType? = null,
@SerialName(fileSizeField)
override val fileSize: Long? = null
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, CustomNamedMediaFile, SizedMediaFile
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, CustomNamedMediaFile, SizedMediaFile,
ExternalReplyInfo.ContentVariant

View File

@ -28,6 +28,6 @@ data class AudioFile(
@SerialName(thumbnailField)
override val thumbnail: PhotoSize? = null
) : TelegramMediaFile, CustomNamedMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, TitledMediaFile,
Performerable
Performerable, ExternalReplyInfo.ContentVariant
fun AudioFile.asVoiceFile() = VoiceFile(fileId, fileUniqueId, duration, mimeType, fileSize)

View File

@ -20,7 +20,7 @@ data class DocumentFile(
override val mimeType: MimeType? = null,
@SerialName(fileNameField)
override val fileName: String? = null
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, CustomNamedMediaFile
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, CustomNamedMediaFile, ExternalReplyInfo.ContentVariant
@Suppress("NOTHING_TO_INLINE")
inline fun TelegramMediaFile.asDocumentFile() = if (this is DocumentFile) {

View File

@ -6,17 +6,20 @@ import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.utils.RiskFeature
import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer
import kotlin.jvm.JvmInline
typealias Photo = List<PhotoSize>
@Serializable
@JvmInline
value class Photo(
val photos: List<PhotoSize>
) : List<PhotoSize> by photos, ExternalReplyInfo.ContentVariant
fun Photo.biggest(): PhotoSize? = maxByOrNull {
it.resolution
}
@RiskFeature
object PhotoSerializer : KSerializer<Photo> by ListSerializer(
PhotoSize.serializer()
)
object PhotoSerializer : KSerializer<Photo> by Photo.serializer()
@Serializable
data class PhotoSize(

View File

@ -34,7 +34,7 @@ data class StickerSurrogate(
// TODO:: Serializer
@Serializable(StickerSerializer::class)
sealed interface Sticker : TelegramMediaFile, SizedMediaFile, ThumbedMediaFile {
sealed interface Sticker : TelegramMediaFile, SizedMediaFile, ThumbedMediaFile, ExternalReplyInfo.ContentVariant {
val emoji: String?
val stickerSetName: StickerSetName?
val stickerFormat: StickerFormat

View File

@ -29,7 +29,8 @@ data class VideoFile(
override val mimeType: MimeType? = null,
@SerialName(fileSizeField)
override val fileSize: Long? = null
) : TelegramMediaFile, CustomNamedMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, SizedMediaFile
) : TelegramMediaFile, CustomNamedMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, SizedMediaFile,
ExternalReplyInfo.ContentVariant
@Suppress("NOTHING_TO_INLINE")
inline fun VideoFile.toTelegramMediaVideo(

View File

@ -18,7 +18,7 @@ data class VideoNoteFile(
override val thumbnail: PhotoSize? = null,
@SerialName(fileSizeField)
override val fileSize: Long? = null
) : TelegramMediaFile, ThumbedMediaFile, PlayableMediaFile, SizedMediaFile {
) : TelegramMediaFile, ThumbedMediaFile, PlayableMediaFile, SizedMediaFile, ExternalReplyInfo.ContentVariant {
override val height: Int
get() = width
}

View File

@ -19,7 +19,7 @@ data class VoiceFile(
override val mimeType: MimeType? = null,
@SerialName(fileSizeField)
override val fileSize: Long? = null
) : TelegramMediaFile, MimedMediaFile, PlayableMediaFile
) : TelegramMediaFile, MimedMediaFile, PlayableMediaFile, ExternalReplyInfo.ContentVariant
fun VoiceFile.asAudioFile(
performer: String? = null,

View File

@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.types.games
import dev.inmo.tgbotapi.abstracts.TextedInput
import dev.inmo.tgbotapi.abstracts.Titled
import dev.inmo.tgbotapi.types.ExternalReplyInfo
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.files.AnimationFile
import dev.inmo.tgbotapi.types.files.Photo
@ -15,4 +16,4 @@ data class Game(
override val text: String? = null,
override val textSources: TextSourcesList = emptyList(),
val animation: AnimationFile? = null
) : Titled, TextedInput
) : Titled, TextedInput, ExternalReplyInfo.ContentVariant

View File

@ -13,7 +13,8 @@ import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable(GiveawayPublicResults.Companion::class)
sealed interface GiveawayPublicResults: GiveawayInfo, GiveawayResults, WithPreviewChatAndMessageId {
sealed interface GiveawayPublicResults: GiveawayInfo, GiveawayResults, WithPreviewChatAndMessageId,
ExternalReplyInfo.ContentVariant {
val count: Int
val winners: List<PreviewUser>
val additionalChats: Int

View File

@ -30,4 +30,4 @@ data class ScheduledGiveaway(
val countries: List<IetfLang>? = null,
@SerialName(premiumSubscriptionMonthCountField)
override val premiumMonths: Int? = null
) : GiveawayInfo
) : GiveawayInfo, ExternalReplyInfo.ContentVariant

View File

@ -20,7 +20,7 @@ import kotlinx.serialization.json.JsonObject
*/
@Serializable(LocationSerializer::class)
@ClassCastsIncluded
sealed interface Location : Locationed, HorizontallyAccured
sealed interface Location : Locationed, HorizontallyAccured, ExternalReplyInfo.ContentVariant
@Serializable
data class StaticLocation(

View File

@ -94,6 +94,8 @@ internal data class RawMessage(
private val invoice: Invoice? = null,
private val dice: Dice? = null,
private val successful_payment: SuccessfulPayment? = null,
private val giveaway: ScheduledGiveaway? = null,
private val giveaway_winners: GiveawayResults? = null,
private val users_shared: UsersShared? = null,
private val chat_shared: ChatShared? = null,
@ -132,8 +134,6 @@ internal data class RawMessage(
// Giveaways
private val giveaway_created: GiveawayCreated? = null,
private val giveaway: ScheduledGiveaway? = null,
private val giveaway_winners: GiveawayResults? = null,
private val giveaway_completed: GiveawayPrivateResults? = null,
) {
private val content: MessageContent? by lazy {
@ -177,7 +177,7 @@ internal data class RawMessage(
adaptedCaptionEntities
)
photo != null -> PhotoContent(
photo.toList(),
photo,
caption,
adaptedCaptionEntities,
has_media_spoiler ?: false
@ -191,7 +191,7 @@ internal data class RawMessage(
venue != null -> VenueContent(venue)
poll != null -> PollContent(poll)
invoice != null -> InvoiceContent(invoice)
giveaway != null -> ScheduledGiveawayContent(giveaway)
giveaway != null -> ScheduledGiveawayContent(chat, messageId, giveaway)
giveaway_winners is GiveawayPublicResults -> GiveawayPublicResultsContent(giveaway_winners)
else -> null
}
@ -202,7 +202,7 @@ internal data class RawMessage(
new_chat_members != null -> NewChatMembers(new_chat_members.toList())
left_chat_member != null -> LeftChatMemberEvent(left_chat_member)
new_chat_title != null -> NewChatTitle(new_chat_title)
new_chat_photo != null -> NewChatPhoto(new_chat_photo.toList())
new_chat_photo != null -> NewChatPhoto(new_chat_photo)
video_chat_started != null -> video_chat_started
video_chat_scheduled != null -> video_chat_scheduled
message_auto_delete_timer_changed != null -> message_auto_delete_timer_changed

View File

@ -1,19 +1,46 @@
package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.abstracts.WithMessageId
import korlibs.time.DateTime
import dev.inmo.tgbotapi.abstracts.WithPreviewChatAndMessageId
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.chat.PreviewChat
import dev.inmo.tgbotapi.types.message.RawMessage
import dev.inmo.tgbotapi.types.threadId
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlin.jvm.JvmInline
@ClassCastsIncluded(excludeRegex = ".*Impl")
interface Message : WithPreviewChatAndMessageId {
val date: DateTime
val metaInfo: MetaInfo
get() = MetaInfo(chat.id, messageId)
@Serializable
@JvmInline
value class MetaInfo(
val chatIdMessageIdThreadId: Triple<ChatIdentifier, MessageId, MessageThreadId?>
) : WithMessageId {
val chatId: ChatIdentifier
get() = chatIdMessageIdThreadId.first
override val messageId: MessageId
get() = chatIdMessageIdThreadId.second
val threadId: MessageThreadId?
get() = chatIdMessageIdThreadId.third
constructor(chatId: ChatIdentifier, messageId: MessageId, threadId: MessageThreadId? = chatId.threadId) : this(Triple(chatId, messageId, threadId))
constructor(chatIdMessageId: Pair<ChatIdentifier, MessageId>, threadId: MessageThreadId? = chatIdMessageId.first.threadId) : this(Triple(chatIdMessageId.first, chatIdMessageId.second, threadId))
fun copy(
chatId: ChatIdentifier = this.chatId, messageId: MessageId = this.messageId, threadId: MessageThreadId? = chatId.threadId
) = MetaInfo(chatId, messageId, threadId)
}
}
interface AccessibleMessage : Message

View File

@ -52,6 +52,8 @@ sealed interface MessageContent: ResendableContent {
subclass(StickerContent::class)
subclass(InvoiceContent::class)
subclass(StoryContent::class)
subclass(GiveawayPublicResultsContent::class)
subclass(ScheduledGiveawayContent::class)
additionalBuilder()
}
@ -139,18 +141,6 @@ sealed interface TextedContent : MessageContent, TextedInput
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

@ -1,5 +1,6 @@
package dev.inmo.tgbotapi.types.message.content
import dev.inmo.tgbotapi.requests.ForwardMessage
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
@ -20,6 +21,13 @@ data class GiveawayPublicResultsContent(
allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup?
): Request<out AccessibleMessage> {
TODO("Not yet implemented")
return ForwardMessage(
giveaway.chat.id,
toChatId = chatId,
messageId = giveaway.messageId,
threadId = messageThreadId,
disableNotification = disableNotification,
protectContent = protectContent
)
}
}

View File

@ -1,9 +1,11 @@
package dev.inmo.tgbotapi.types.message.content
import dev.inmo.micro_utils.language_codes.IetfLang
import dev.inmo.tgbotapi.requests.ForwardMessage
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.chat.PreviewChat
import dev.inmo.tgbotapi.types.giveaway.GiveawayInfo
import dev.inmo.tgbotapi.types.giveaway.ScheduledGiveaway
@ -13,6 +15,8 @@ import kotlinx.serialization.Serializable
@Serializable
data class ScheduledGiveawayContent(
private val chat: Chat,
private val messageId: MessageId,
val giveaway: ScheduledGiveaway
) : MessageContent {
override fun createResend(
@ -24,6 +28,13 @@ data class ScheduledGiveawayContent(
allowSendingWithoutReply: Boolean?,
replyMarkup: KeyboardMarkup?
): Request<out AccessibleMessage> {
TODO("Not yet implemented")
return ForwardMessage(
chat.id,
toChatId = chatId,
messageId = messageId,
threadId = messageThreadId,
disableNotification = disableNotification,
protectContent = protectContent
)
}
}

View File

@ -17,4 +17,4 @@ data class Invoice(
override val currency: Currency,
@SerialName(totalAmountField)
override val amount: Long
) : Amounted, Currencied
) : Amounted, Currencied, ExternalReplyInfo.ContentVariant

View File

@ -49,7 +49,7 @@ val LongSeconds.asExactScheduledCloseInfo
@Serializable(PollSerializer::class)
@ClassCastsIncluded
sealed interface Poll {
sealed interface Poll : ExternalReplyInfo.ContentVariant {
val id: PollIdentifier
val question: String
val options: List<PollOption>

View File

@ -1,6 +1,7 @@
package dev.inmo.tgbotapi.types.stories
import dev.inmo.tgbotapi.types.ExternalReplyInfo
import kotlinx.serialization.Serializable
@Serializable
class Story
class Story : ExternalReplyInfo.ContentVariant

View File

@ -23,4 +23,4 @@ data class Venue(
override val googlePlaceId: GooglePlaceId? = null,
@SerialName(googlePlaceTypeField)
override val googlePlaceType: GooglePlaceType? = null
) : CommonVenueData, Locationed by location
) : CommonVenueData, Locationed by location, ExternalReplyInfo.ContentVariant