diff --git a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/gifts/GetAvailableGifts.kt b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/gifts/GetAvailableGifts.kt new file mode 100644 index 0000000000..722384f185 --- /dev/null +++ b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/gifts/GetAvailableGifts.kt @@ -0,0 +1,7 @@ +package dev.inmo.tgbotapi.extensions.api.gifts + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.gifts.GetAvailableGifts +import dev.inmo.tgbotapi.types.gifts.Gifts + +public suspend fun TelegramBot.getAvailableGifts(): Gifts = execute(GetAvailableGifts) diff --git a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/gifts/SendGift.kt b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/gifts/SendGift.kt new file mode 100644 index 0000000000..5f044bcbe0 --- /dev/null +++ b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/gifts/SendGift.kt @@ -0,0 +1,103 @@ +package dev.inmo.tgbotapi.extensions.api.gifts + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.gifts.SendGift +import dev.inmo.tgbotapi.types.GiftId +import dev.inmo.tgbotapi.types.UserId +import dev.inmo.tgbotapi.types.chat.User +import dev.inmo.tgbotapi.types.gifts.Gift +import dev.inmo.tgbotapi.types.message.ParseMode +import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList + +public suspend fun TelegramBot.sendGift( + userId: UserId, + giftId: GiftId, + text: String, + parseMode: ParseMode? +): Boolean = execute( + SendGift( + userId, + giftId, + text, + parseMode + ) +) + +public suspend fun TelegramBot.sendGift( + userId: UserId, + giftId: GiftId, + textSources: TextSourcesList, +): Boolean = execute( + SendGift( + userId, + giftId, + textSources + ) +) + +public suspend fun TelegramBot.sendGift( + user: User, + giftId: GiftId, + text: String, + parseMode: ParseMode? +): Boolean = sendGift( + user.id, + giftId, + text, + parseMode +) + +public suspend fun TelegramBot.sendGift( + user: User, + giftId: GiftId, + textSources: TextSourcesList, +): Boolean = sendGift( + user.id, + giftId, + textSources +) + +public suspend fun TelegramBot.sendGift( + user: UserId, + gift: Gift, + text: String, + parseMode: ParseMode? +): Boolean = sendGift( + user, + gift.id, + text, + parseMode +) + +public suspend fun TelegramBot.sendGift( + user: UserId, + gift: Gift, + textSources: TextSourcesList, +): Boolean = sendGift( + user, + gift.id, + textSources +) + + +public suspend fun TelegramBot.sendGift( + user: User, + gift: Gift, + text: String, + parseMode: ParseMode? +): Boolean = sendGift( + user.id, + gift.id, + text, + parseMode +) + +public suspend fun TelegramBot.sendGift( + user: User, + gift: Gift, + textSources: TextSourcesList, +): Boolean = sendGift( + user.id, + gift.id, + textSources +) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/gifts/GetAvailableGifts.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/gifts/GetAvailableGifts.kt new file mode 100644 index 0000000000..7f0a8434c9 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/gifts/GetAvailableGifts.kt @@ -0,0 +1,17 @@ +package dev.inmo.tgbotapi.requests.gifts + +import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest +import dev.inmo.tgbotapi.types.gifts.Gifts +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationStrategy + +@Serializable +data object GetAvailableGifts : SimpleRequest { + override fun method(): String = "getAvailableGifts" + + override val resultDeserializer: DeserializationStrategy + get() = Gifts.serializer() + override val requestSerializer: SerializationStrategy<*> + get() = serializer() +} \ No newline at end of file diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/gifts/SendGift.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/gifts/SendGift.kt new file mode 100644 index 0000000000..5f5369e3bd --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/gifts/SendGift.kt @@ -0,0 +1,57 @@ +package dev.inmo.tgbotapi.requests.gifts + +import dev.inmo.tgbotapi.abstracts.TextedOutput +import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest +import dev.inmo.tgbotapi.types.* +import dev.inmo.tgbotapi.types.message.ParseMode +import dev.inmo.tgbotapi.types.message.RawMessageEntity +import dev.inmo.tgbotapi.types.message.asTextSources +import dev.inmo.tgbotapi.types.message.textsources.TextSource +import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList +import dev.inmo.tgbotapi.types.message.toRawMessageEntities +import dev.inmo.tgbotapi.utils.extensions.makeSourceString +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.builtins.serializer + +@Serializable +data class SendGift internal constructor( + @SerialName(userIdField) + val userId: UserId, + @SerialName(giftIdField) + val giftId: GiftId, + @SerialName(textField) + override val text: String, + @SerialName(textParseModeField) + override val parseMode: ParseMode?, + @SerialName(entitiesField) + private val rawEntities: List? = null, +) : SimpleRequest, TextedOutput { + override val textSources: TextSourcesList? by lazy { + rawEntities ?.asTextSources(text) + } + + override fun method(): String = "sendGift" + + override val requestSerializer: SerializationStrategy<*> + get() = serializer() + + + override val resultDeserializer: DeserializationStrategy + get() = Boolean.serializer() + + constructor( + userId: UserId, + giftId: GiftId, + text: String, + parseMode: ParseMode? + ) : this(userId, giftId, text, parseMode, null) + + constructor( + userId: UserId, + giftId: GiftId, + textSources: TextSourcesList, + ) : this(userId, giftId, textSources.makeSourceString(), null, textSources.toRawMessageEntities()) +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt index 324ac6a3a3..f333ce0ee4 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt @@ -34,6 +34,12 @@ value class PaidMediaPayload(val string: String) { } } +@Serializable +@JvmInline +value class GiftId( + val string: String +) + val usernameRegex = Regex("@[\\w\\d_]+") val degreesLimit = 1 .. 360 @@ -190,6 +196,7 @@ const val maskPositionField = "mask_position" const val phoneNumberField = "phone_number" const val userIdField = "user_id" const val userIdsField = "user_ids" +const val giftIdField = "gift_id" const val onlyIfBannedField = "only_if_banned" const val containsMasksField = "contains_masks" const val resultIdField = "result_id" @@ -472,6 +479,8 @@ const val creatorField = "creator" const val subscriptionPeriodField = "subscription_period" const val subscriptionPriceField = "subscription_price" const val copyTextField = "copy_text" +const val giftField = "gift" +const val giftsField = "gifts" const val pointField = "point" const val xShiftField = "x_shift" @@ -512,6 +521,7 @@ const val shippingOptionsField = "shipping_options" const val countryCodeField = "country_code" const val countryCodesField = "country_codes" const val totalCountField = "total_count" +const val remainingCountField = "remaining_count" const val stateField = "state" const val cityField = "city" const val firstStreetLineField = "street_line1" diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/gifts/Gift.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/gifts/Gift.kt new file mode 100644 index 0000000000..a8398160ab --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/gifts/Gift.kt @@ -0,0 +1,92 @@ +package dev.inmo.tgbotapi.types.gifts + +import dev.inmo.tgbotapi.types.* +import dev.inmo.tgbotapi.types.files.Sticker +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(Gift.Companion::class) +sealed interface Gift { + val id: GiftId + val sticker: Sticker + val starCount: Int + val totalCount: Int? + val remainingCount: Int? + + @Serializable + data class Unlimited( + @SerialName(idField) + override val id: GiftId, + @SerialName(stickerField) + override val sticker: Sticker, + @SerialName(starCountField) + override val starCount: Int + ) : Gift { + override val totalCount: Int? + get() = null + override val remainingCount: Int? + get() = null + } + + @Serializable + data class Limited( + @SerialName(idField) + override val id: GiftId, + @SerialName(stickerField) + override val sticker: Sticker, + @SerialName(starCountField) + override val starCount: Int, + @SerialName(totalCountField) + override val totalCount: Int, + @SerialName(remainingCountField) + override val remainingCount: Int, + ) : Gift + + companion object : KSerializer { + @Serializable + private data class GiftSurrogate( + val id: GiftId, + val sticker: Sticker, + val star_count: Int, + val total_count: Int? = null, + val remaining_count: Int? = null, + ) + + override val descriptor: SerialDescriptor + get() = GiftSurrogate.serializer().descriptor + + override fun deserialize(decoder: Decoder): Gift { + val surrogate = GiftSurrogate.serializer().deserialize(decoder) + + return if (surrogate.total_count != null && surrogate.remaining_count != null) { + Limited( + surrogate.id, + surrogate.sticker, + surrogate.star_count, + surrogate.total_count, + surrogate.remaining_count + ) + } else { + Unlimited( + surrogate.id, + surrogate.sticker, + surrogate.star_count, + ) + } + } + + override fun serialize(encoder: Encoder, value: Gift) { + val surrogate = GiftSurrogate( + value.id, + value.sticker, + value.starCount, + value.totalCount, + value.remainingCount + ) + } + } +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/gifts/Gifts.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/gifts/Gifts.kt new file mode 100644 index 0000000000..07ae5c6fd1 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/gifts/Gifts.kt @@ -0,0 +1,11 @@ +package dev.inmo.tgbotapi.types.gifts + +import dev.inmo.tgbotapi.types.giftsField +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Gifts( + @SerialName(giftsField) + val gifts: List +) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/payments/stars/TransactionPartner.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/payments/stars/TransactionPartner.kt index 90cf454d9a..12fea54bad 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/payments/stars/TransactionPartner.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/payments/stars/TransactionPartner.kt @@ -4,6 +4,7 @@ package dev.inmo.tgbotapi.types.payments.stars import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.chat.PreviewUser +import dev.inmo.tgbotapi.types.gifts.Gift import dev.inmo.tgbotapi.types.message.payments.PaidMedia import dev.inmo.tgbotapi.utils.decodeDataAndJson import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded @@ -45,7 +46,9 @@ sealed interface TransactionPartner { @SerialName(paidMediaField) val paidMedia: List? = null, @SerialName(paidMediaPayloadField) - val paidMediaPayload: PaidMediaPayload? = null + val paidMediaPayload: PaidMediaPayload? = null, + @SerialName(giftField) + val gift: Gift? = null ) : TransactionPartner { @EncodeDefault override val type: String = Companion.type