From c4f2566b7113937d7fafd1bc81a0c9d0ce5d34c9 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 16 Feb 2026 22:57:45 +0600 Subject: [PATCH] add support of style and iconEmojiCustomId --- .../kotlin/dev/inmo/tgbotapi/types/Common.kt | 2 + .../InlineKeyboardButton.kt | 68 +++++++++++-- .../tgbotapi/types/buttons/KeyboardButton.kt | 99 ++++++++++++++++--- .../types/buttons/KeyboardButtonStyle.kt | 51 ++++++++++ 4 files changed, 194 insertions(+), 26 deletions(-) create mode 100644 tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonStyle.kt 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 574fb863b1..6d242b0521 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 @@ -329,6 +329,8 @@ const val checklistMessageField = "checklist_message" const val markedAsDoneTaskIdsField = "marked_as_done_task_ids" const val markedAsNotDoneTaskIdsField = "marked_as_not_done_task_ids" +const val styleField = "style" + const val requestContactField = "request_contact" const val requestLocationField = "request_location" const val requestPollField = "request_poll" diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/InlineKeyboardButtons/InlineKeyboardButton.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/InlineKeyboardButtons/InlineKeyboardButton.kt index 33a22297f8..c2e113cb96 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/InlineKeyboardButtons/InlineKeyboardButton.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/InlineKeyboardButtons/InlineKeyboardButton.kt @@ -5,6 +5,7 @@ package dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded import dev.inmo.tgbotapi.types.* +import dev.inmo.tgbotapi.types.buttons.KeyboardButtonStyle import dev.inmo.tgbotapi.types.games.CallbackGame import dev.inmo.tgbotapi.types.webapps.WebAppInfo import kotlinx.serialization.* @@ -18,6 +19,9 @@ import kotlinx.serialization.json.* @ClassCastsIncluded sealed interface InlineKeyboardButton { val text: String + + val style: KeyboardButtonStyle? + val iconCustomEmojiId: CustomEmojiId? } @Serializable @@ -28,6 +32,10 @@ data class UnknownInlineKeyboardButton ( get() = runCatching { rawData.jsonObject[textField] ?.jsonPrimitive ?.content }.getOrNull() ?: "" + override val style: KeyboardButtonStyle? + get() = null + override val iconCustomEmojiId: CustomEmojiId? + get() = null } /** @@ -36,7 +44,11 @@ data class UnknownInlineKeyboardButton ( */ @Serializable data class PayInlineKeyboardButton( - override val text: String + override val text: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton { @ExperimentalSerializationApi @EncodeDefault @@ -58,7 +70,11 @@ data class CallbackDataInlineKeyboardButton( * You will receive this data in [dev.inmo.tgbotapi.types.queries.callback.DataCallbackQuery.data] field */ @SerialName(callbackDataField) - val callbackData: String + val callbackData: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton /** @@ -67,7 +83,11 @@ data class CallbackDataInlineKeyboardButton( @Serializable data class CallbackGameInlineKeyboardButton( @SerialName(textField) - override val text: String + override val text: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton { @SerialName(callbackGameField) @EncodeDefault @@ -81,7 +101,11 @@ data class CallbackGameInlineKeyboardButton( data class LoginURLInlineKeyboardButton( override val text: String, @SerialName(loginUrlField) - val loginUrl: LoginURL + val loginUrl: LoginURL, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton /** @@ -98,7 +122,11 @@ data class LoginURLInlineKeyboardButton( data class SwitchInlineQueryCurrentChatInlineKeyboardButton( override val text: String, @SerialName(switchInlineQueryCurrentChatField) - val switchInlineQueryCurrentChat: String + val switchInlineQueryCurrentChat: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton /** @@ -115,7 +143,11 @@ data class SwitchInlineQueryCurrentChatInlineKeyboardButton( data class SwitchInlineQueryChosenChatInlineKeyboardButton( override val text: String, @SerialName(switchInlineQueryChosenChatField) - val parameters: SwitchInlineQueryChosenChat + val parameters: SwitchInlineQueryChosenChat, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton /** @@ -131,7 +163,11 @@ data class SwitchInlineQueryChosenChatInlineKeyboardButton( data class SwitchInlineQueryInlineKeyboardButton( override val text: String, @SerialName(switchInlineQueryField) - val switchInlineQuery: String + val switchInlineQuery: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton /** @@ -141,7 +177,11 @@ data class SwitchInlineQueryInlineKeyboardButton( data class URLInlineKeyboardButton( override val text: String, @SerialName(urlField) - val url: String + val url: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton /** @@ -151,7 +191,11 @@ data class URLInlineKeyboardButton( data class CopyTextButton( override val text: String, @SerialName(copyTextField) - val data: CopyTextButtonData + val data: CopyTextButtonData, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton /** @@ -162,5 +206,9 @@ data class CopyTextButton( data class WebAppInlineKeyboardButton( override val text: String, @SerialName(webAppField) - val webApp: WebAppInfo + val webApp: WebAppInfo, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : InlineKeyboardButton diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButton.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButton.kt index 9fdff29f3d..f23e77c9b9 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButton.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButton.kt @@ -19,6 +19,9 @@ import kotlinx.serialization.json.* @Serializable(KeyboardButtonSerializer::class) sealed interface KeyboardButton { val text: String + + val style: KeyboardButtonStyle? + val iconCustomEmojiId: CustomEmojiId? } /** @@ -30,7 +33,11 @@ sealed interface KeyboardButton { */ @Serializable data class SimpleKeyboardButton( - override val text: String + override val text: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : KeyboardButton @ConsistentCopyVisibility @@ -38,7 +45,12 @@ data class SimpleKeyboardButton( data class UnknownKeyboardButton internal constructor( override val text: String, val raw: String -) : KeyboardButton +) : KeyboardButton { + override val style: KeyboardButtonStyle? + get() = null + override val iconCustomEmojiId: CustomEmojiId? + get() = null +} /** * Private chats only. When user will tap on this button, his contact (with his number and name) will be sent to the bot. You will be able @@ -49,7 +61,11 @@ data class UnknownKeyboardButton internal constructor( */ @Serializable data class RequestContactKeyboardButton( - override val text: String + override val text: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : KeyboardButton { @SerialName(requestContactField) @EncodeDefault @@ -65,7 +81,11 @@ data class RequestContactKeyboardButton( */ @Serializable data class RequestLocationKeyboardButton( - override val text: String + override val text: String, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : KeyboardButton { @SerialName(requestLocationField) @Required @@ -82,7 +102,11 @@ data class RequestLocationKeyboardButton( data class WebAppKeyboardButton( override val text: String, @SerialName(webAppField) - val webApp: WebAppInfo + val webApp: WebAppInfo, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : KeyboardButton /** @@ -96,7 +120,11 @@ data class WebAppKeyboardButton( data class RequestPollKeyboardButton( override val text: String, @SerialName(requestPollField) - val requestPoll: KeyboardButtonPollType + val requestPoll: KeyboardButtonPollType, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : KeyboardButton /** @@ -114,7 +142,11 @@ data class RequestPollKeyboardButton( data class RequestUserKeyboardButton( override val text: String, @SerialName(requestUsersField) - val requestUsers: KeyboardButtonRequestUsers + val requestUsers: KeyboardButtonRequestUsers, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : KeyboardButton /** @@ -132,7 +164,11 @@ data class RequestUserKeyboardButton( data class RequestChatKeyboardButton( override val text: String, @SerialName(requestChatField) - val requestChat: KeyboardButtonRequestChat + val requestChat: KeyboardButtonRequestChat, + @SerialName(iconCustomEmojiIdField) + override val iconCustomEmojiId: CustomEmojiId? = null, + @SerialName(styleField) + override val style: KeyboardButtonStyle? = null ) : KeyboardButton @RiskFeature @@ -142,42 +178,73 @@ object KeyboardButtonSerializer : KSerializer { override fun deserialize(decoder: Decoder): KeyboardButton { val asJson = internalSerializer.deserialize(decoder) + val styleData: KeyboardButtonStyle? by lazy { + (asJson as? JsonObject) + ?.get(styleField) + ?.jsonPrimitive + ?.let { nonstrictJsonFormat.decodeFromJsonElement(KeyboardButtonStyle.serializer(), it) } + } + val iconCustomEmojiIdData: CustomEmojiId? by lazy { + (asJson as? JsonObject) + ?.get(iconCustomEmojiIdField) + ?.jsonPrimitive + ?.let { nonstrictJsonFormat.decodeFromJsonElement(CustomEmojiId.serializer(), it) } + } return when { - asJson is JsonPrimitive -> SimpleKeyboardButton(asJson.content) + asJson is JsonPrimitive -> SimpleKeyboardButton( + asJson.content + ) asJson is JsonObject && asJson[requestContactField] != null -> RequestContactKeyboardButton( - asJson[textField]!!.jsonPrimitive.content + asJson[textField]!!.jsonPrimitive.content, + iconCustomEmojiIdData, + styleData ) asJson is JsonObject && asJson[requestLocationField] != null -> RequestLocationKeyboardButton( - asJson[textField]!!.jsonPrimitive.content + asJson[textField]!!.jsonPrimitive.content, + iconCustomEmojiIdData, + styleData ) asJson is JsonObject && asJson[webAppField] != null -> WebAppKeyboardButton( asJson[textField]!!.jsonPrimitive.content, nonstrictJsonFormat.decodeFromJsonElement( WebAppInfo.serializer(), asJson[webAppField]!! - ) + ), + iconCustomEmojiIdData, + styleData ) asJson is JsonObject && asJson[requestPollField] != null -> RequestPollKeyboardButton( asJson[textField]!!.jsonPrimitive.content, nonstrictJsonFormat.decodeFromJsonElement( KeyboardButtonPollTypeSerializer, asJson[requestPollField] ?.jsonObject ?: buildJsonObject { } - ) + ), + iconCustomEmojiIdData, + styleData ) asJson is JsonObject && asJson[requestUsersField] != null -> RequestUserKeyboardButton( asJson[textField]!!.jsonPrimitive.content, nonstrictJsonFormat.decodeFromJsonElement( KeyboardButtonRequestUsers.serializer(), asJson[requestUsersField] ?.jsonObject ?: buildJsonObject { } - ) + ), + iconCustomEmojiIdData, + styleData ) asJson is JsonObject && asJson[requestChatField] != null -> RequestChatKeyboardButton( asJson[textField]!!.jsonPrimitive.content, nonstrictJsonFormat.decodeFromJsonElement( KeyboardButtonRequestChat.serializer(), asJson[requestChatField] ?.jsonObject ?: buildJsonObject { } - ) + ), + iconCustomEmojiIdData, + styleData + ) + asJson is JsonObject && asJson[textField] != null -> SimpleKeyboardButton( + asJson[textField]!!.jsonPrimitive.content, + iconCustomEmojiIdData, + styleData ) else -> UnknownKeyboardButton( when (asJson) { @@ -196,7 +263,7 @@ object KeyboardButtonSerializer : KSerializer { is RequestLocationKeyboardButton -> RequestLocationKeyboardButton.serializer().serialize(encoder, value) is WebAppKeyboardButton -> WebAppKeyboardButton.serializer().serialize(encoder, value) is RequestPollKeyboardButton -> RequestPollKeyboardButton.serializer().serialize(encoder, value) - is SimpleKeyboardButton -> encoder.encodeString(value.text) + is SimpleKeyboardButton -> SimpleKeyboardButton.serializer().serialize(encoder, value) is RequestUserKeyboardButton -> RequestUserKeyboardButton.serializer().serialize(encoder, value) is RequestChatKeyboardButton -> RequestChatKeyboardButton.serializer().serialize(encoder, value) is UnknownKeyboardButton -> JsonElement.serializer().serialize(encoder, nonstrictJsonFormat.parseToJsonElement(value.raw)) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonStyle.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonStyle.kt new file mode 100644 index 0000000000..05027f4d9b --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonStyle.kt @@ -0,0 +1,51 @@ +package dev.inmo.tgbotapi.types.buttons + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@Serializable(KeyboardButtonStyle.Serializer::class) +sealed interface KeyboardButtonStyle { + val name: String + + @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") + @Serializable(KeyboardButtonStyle.Serializer::class) + data object Danger : KeyboardButtonStyle { override val name: String = "danger" } + + @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") + @Serializable(KeyboardButtonStyle.Serializer::class) + data object Success : KeyboardButtonStyle { override val name: String = "success" } + + @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") + @Serializable(KeyboardButtonStyle.Serializer::class) + data object Primary : KeyboardButtonStyle { override val name: String = "primary" } + + @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") + @Serializable(KeyboardButtonStyle.Serializer::class) + data class Custom(override val name: String = "primary") : KeyboardButtonStyle + + object Serializer : KSerializer { + override val descriptor: SerialDescriptor + get() = String.serializer().descriptor + + override fun serialize( + encoder: Encoder, + value: KeyboardButtonStyle + ) { + encoder.encodeString(value.name) + } + + override fun deserialize(decoder: Decoder): KeyboardButtonStyle { + return when (val rawValue = decoder.decodeString()) { + Danger.name -> Danger + Success.name -> Success + Primary.name -> Primary + else -> Custom(rawValue) + } + } + + } +} \ No newline at end of file