package dev.inmo.tgbotapi.types.buttons import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.webapps.WebAppInfo import dev.inmo.tgbotapi.utils.RiskFeature import dev.inmo.tgbotapi.utils.nonstrictJsonFormat import kotlinx.serialization.* import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.* /** * Representation union of https://core.telegram.org/bots/api#keyboardbutton . See inheritors for more info */ @Serializable(KeyboardButtonSerializer::class) sealed interface KeyboardButton { val text: String } /** * Simple button. user will send text of this button. You will be able to catch this text in updates and data using * [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onText] in * case you are using Behaviour Builder OR with [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter.messagesFlow] * and [kotlinx.coroutines.flow.filterIsInstance] and filtering by type * [dev.inmo.tgbotapi.types.message.abstracts.CommonMessage] and [dev.inmo.tgbotapi.extensions.utils.onlyTextContentMessages] */ @Serializable data class SimpleKeyboardButton( override val text: String ) : KeyboardButton @Serializable data class UnknownKeyboardButton internal constructor( override val text: String, val raw: String ) : KeyboardButton /** * 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 * to catch this contact in updates and data using [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContact] in * case you are using Behaviour Builder OR with [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter.messagesFlow] * and [kotlinx.coroutines.flow.filterIsInstance] and filtering by type * [dev.inmo.tgbotapi.types.message.abstracts.CommonMessage] and [dev.inmo.tgbotapi.extensions.utils.onlyContactContentMessages] */ @Serializable data class RequestContactKeyboardButton( override val text: String ) : KeyboardButton { @SerialName(requestContactField) @EncodeDefault val requestContact: Boolean = true } /** * Private chats only. When user will tap on this button, his location will be sent to the bot. You will be able * to catch this location in updates and data using [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onLocation] in * case you are using Behaviour Builder OR with [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter.messagesFlow] * and [kotlinx.coroutines.flow.filterIsInstance] and filtering by type * [dev.inmo.tgbotapi.types.message.abstracts.CommonMessage] and [dev.inmo.tgbotapi.extensions.utils.onlyLocationContentMessages] */ @Serializable data class RequestLocationKeyboardButton( override val text: String ) : KeyboardButton { @SerialName(requestLocationField) @Required val requestLocation: Boolean = true } /** * Private chats only. Description of the Web App that will be launched when the user presses the button. The Web App * will be able to send an arbitrary message on behalf of the user using the method `answerWebAppQuery`. Available only * in private chats between a user and the bot. */ @Serializable data class WebAppKeyboardButton( override val text: String, @SerialName(webAppField) val webApp: WebAppInfo ) : KeyboardButton /** * Private chats only. When user will tap on this button, he will be asked for the poll with [requestPoll] options. You will be able * to catch this poll in updates and data using [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPoll] in * case you are using Behaviour Builder OR with [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter.messagesFlow] * and [kotlinx.coroutines.flow.filterIsInstance] and filtering by type * [dev.inmo.tgbotapi.types.message.abstracts.CommonMessage] and [dev.inmo.tgbotapi.extensions.utils.onlyPollContentMessages] */ @Serializable data class RequestPollKeyboardButton( override val text: String, @SerialName(requestPollField) val requestPoll: KeyboardButtonPollType ) : KeyboardButton /** * Private chats only. When user will tap on this button, he will be asked for the chat with [requestChat] options. You * will be able to catch this [ChatId] in updates and data using * [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUserShared] in case you are using Behaviour * Builder OR with [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter.messagesFlow] * and [kotlinx.coroutines.flow.filterIsInstance]. * * In case you will use [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUserShared] it is * recommended to use [kotlinx.coroutines.flow.Flow] [kotlinx.coroutines.flow.filter] with checking of incoming * [dev.inmo.tgbotapi.types.request.UserShared.requestId] */ @Serializable data class RequestUserKeyboardButton( override val text: String, @SerialName(requestUserField) val requestUser: KeyboardButtonRequestUser ) : KeyboardButton /** * Private chats only. When user will tap on this button, he will be asked for the chat with [requestChat] options. You * will be able to catch this [ChatId] in updates and data using * [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared] in case you are using Behaviour * Builder OR with [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter.messagesFlow] * and [kotlinx.coroutines.flow.filterIsInstance]. * * In case you will use [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared] it is * recommended to use [kotlinx.coroutines.flow.Flow] [kotlinx.coroutines.flow.filter] with checking of incoming * [dev.inmo.tgbotapi.types.request.ChatShared.requestId] */ @Serializable data class RequestChatKeyboardButton( override val text: String, @SerialName(requestChatField) val requestChat: KeyboardButtonRequestChat ) : KeyboardButton @RiskFeature object KeyboardButtonSerializer : KSerializer { private val internalSerializer = JsonElement.serializer() override val descriptor: SerialDescriptor = internalSerializer.descriptor override fun deserialize(decoder: Decoder): KeyboardButton { val asJson = internalSerializer.deserialize(decoder) return when { asJson is JsonPrimitive -> SimpleKeyboardButton(asJson.content) asJson is JsonObject && asJson[requestContactField] != null -> RequestContactKeyboardButton( asJson[textField]!!.jsonPrimitive.content ) asJson is JsonObject && asJson[requestLocationField] != null -> RequestLocationKeyboardButton( asJson[textField]!!.jsonPrimitive.content ) asJson is JsonObject && asJson[webAppField] != null -> WebAppKeyboardButton( asJson[textField]!!.jsonPrimitive.content, nonstrictJsonFormat.decodeFromJsonElement( WebAppInfo.serializer(), asJson[webAppField]!! ) ) asJson is JsonObject && asJson[requestPollField] != null -> RequestPollKeyboardButton( asJson[textField]!!.jsonPrimitive.content, nonstrictJsonFormat.decodeFromJsonElement( KeyboardButtonPollTypeSerializer, asJson[requestPollField] ?.jsonObject ?: buildJsonObject { } ) ) asJson is JsonObject && asJson[requestUserField] != null -> RequestUserKeyboardButton( asJson[textField]!!.jsonPrimitive.content, nonstrictJsonFormat.decodeFromJsonElement( KeyboardButtonRequestUser.serializer(), asJson[requestUserField] ?.jsonObject ?: buildJsonObject { } ) ) asJson is JsonObject && asJson[requestChatField] != null -> RequestChatKeyboardButton( asJson[textField]!!.jsonPrimitive.content, nonstrictJsonFormat.decodeFromJsonElement( KeyboardButtonRequestChat.serializer(), asJson[requestChatField] ?.jsonObject ?: buildJsonObject { } ) ) else -> UnknownKeyboardButton( when (asJson) { is JsonObject -> asJson[textField]!!.jsonPrimitive.content is JsonArray -> "" is JsonPrimitive -> asJson.content }, asJson.toString() ) } } override fun serialize(encoder: Encoder, value: KeyboardButton) { when (value) { is RequestContactKeyboardButton -> RequestContactKeyboardButton.serializer().serialize(encoder, value) 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 RequestUserKeyboardButton -> RequestUserKeyboardButton.serializer().serialize(encoder, value) is RequestChatKeyboardButton -> RequestChatKeyboardButton.serializer().serialize(encoder, value) is UnknownKeyboardButton -> JsonElement.serializer().serialize(encoder, nonstrictJsonFormat.parseToJsonElement(value.raw)) } } }