diff --git a/CHANGELOG.md b/CHANGELOG.md index 3697567e16..28e54c384a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,24 @@ ## 5.1.0 +[Bot API 6.5](https://core.telegram.org/bots/api-changelog#february-3-2023) support + * `Core`: * [Bot API 6.5](https://core.telegram.org/bots/api#february-3-2023) support - * Replaced the fields `can_send_media_messages` in the classes `ChatMemberRestricted` and `ChatPermissions` with separate fields `can_send_audios`, `can_send_documents`, `can_send_photos`, `can_send_videos`, `can_send_video_notes`, and `can_send_voice_notes` for different media types. + +## 5.0.2 + +* `Versions`: + * `MicroUtils`: `0.16.6` -> `0.16.8` + * `Ktor`: `2.2.2` -> `2.2.3` +* `BehaviourBuilder`: + * Fixes in `BehaviourContext.onEditedContentMessage` - now it will trigger callback on channel post edits too ## 5.0.1 * `Versions`: - * `MicroUtils`: `0.16.4` -> `0.16.6` - * `Ktor`: `2.2.1` -> `2.2.2` + * `MicroUtils`: `0.16.4` -> `0.16.6` + * `Ktor`: `2.2.1` -> `2.2.2` * `Core`: * Fixes in `SendMediaGroup` request * Fixes in `SetChatAdministratorCustomTitle` request (thanks to [@madhead](https://github.com/madhead)) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index faa81a5246..caca9c0f70 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -kotlin = "1.8.10" +kotlin = "1.7.22" kotlin-serialization = "1.4.1" kotlin-coroutines = "1.6.4" diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitEventAction.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitEventAction.kt index ec6e5f8572..2fb21d12f3 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitEventAction.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitEventAction.kt @@ -17,6 +17,9 @@ import dev.inmo.tgbotapi.types.message.ChatEvents.forum.WriteAccessAllowed import dev.inmo.tgbotapi.types.message.ChatEvents.voice.* import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage import dev.inmo.tgbotapi.types.message.payments.SuccessfulPaymentEvent +import dev.inmo.tgbotapi.types.request.ChatShared +import dev.inmo.tgbotapi.types.request.ChatSharedRequest +import dev.inmo.tgbotapi.types.request.UserShared import dev.inmo.tgbotapi.utils.RiskFeature import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage import kotlinx.coroutines.flow.Flow @@ -171,3 +174,18 @@ suspend fun BehaviourContext.waitWriteAccessAllowed( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } ) = waitEvents(initRequest, errorFactory) + +suspend fun BehaviourContext.waitChatSharedRequest( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEvents(initRequest, errorFactory) + +suspend fun BehaviourContext.waitUserShared( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEvents(initRequest, errorFactory) + +suspend fun BehaviourContext.waitChatShared( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEvents(initRequest, errorFactory) diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitEventActionMessages.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitEventActionMessages.kt index a26a4215ad..9539f3d2fd 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitEventActionMessages.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitEventActionMessages.kt @@ -9,10 +9,17 @@ import dev.inmo.tgbotapi.types.message.ChatEvents.* import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.* import dev.inmo.tgbotapi.types.message.ChatEvents.forum.ForumTopicClosed import dev.inmo.tgbotapi.types.message.ChatEvents.forum.ForumTopicCreated +import dev.inmo.tgbotapi.types.message.ChatEvents.forum.ForumTopicEdited import dev.inmo.tgbotapi.types.message.ChatEvents.forum.ForumTopicReopened +import dev.inmo.tgbotapi.types.message.ChatEvents.forum.GeneralForumTopicHidden +import dev.inmo.tgbotapi.types.message.ChatEvents.forum.GeneralForumTopicUnhidden +import dev.inmo.tgbotapi.types.message.ChatEvents.forum.WriteAccessAllowed import dev.inmo.tgbotapi.types.message.ChatEvents.voice.* import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage import dev.inmo.tgbotapi.types.message.payments.SuccessfulPaymentEvent +import dev.inmo.tgbotapi.types.request.ChatShared +import dev.inmo.tgbotapi.types.request.ChatSharedRequest +import dev.inmo.tgbotapi.types.request.UserShared import dev.inmo.tgbotapi.utils.RiskFeature import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage import kotlinx.coroutines.flow.Flow @@ -148,3 +155,34 @@ suspend fun BehaviourContext.waitForumTopicReopenedEventsMessages( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } ) = waitEventsMessages(initRequest, errorFactory) +suspend fun BehaviourContext.waitForumTopicEditedEventsMessages( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEventsMessages(initRequest, errorFactory) +suspend fun BehaviourContext.waitGeneralForumTopicHiddenEventsMessages( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEventsMessages(initRequest, errorFactory) +suspend fun BehaviourContext.waitGeneralForumTopicUnhiddenEventsMessages( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEventsMessages(initRequest, errorFactory) +suspend fun BehaviourContext.waitWriteAccessAllowedEventsMessages( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEventsMessages(initRequest, errorFactory) + +suspend fun BehaviourContext.waitChatSharedRequestEventsMessages( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEventsMessages(initRequest, errorFactory) + +suspend fun BehaviourContext.waitUserSharedEventsMessages( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEventsMessages(initRequest, errorFactory) + +suspend fun BehaviourContext.waitChatSharedEventsMessages( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitEventsMessages(initRequest, errorFactory) diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EditedContentTriggers.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EditedContentTriggers.kt index 0b6337d018..3141a57439 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EditedContentTriggers.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EditedContentTriggers.kt @@ -1,18 +1,3 @@ - - -/** - * @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call - * @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example, - * this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage]. - * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own. - * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times] - * to combinate several filters - * @param [markerFactory] Will be used to identify different "stream". [scenarioReceiver] will be called synchronously - * in one "stream". Output of [markerFactory] will be used as a key for "stream" - * @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that - * data - */@file:Suppress("unused", "UNCHECKED_CAST") - package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling import dev.inmo.tgbotapi.extensions.behaviour_builder.* @@ -35,7 +20,7 @@ internal suspend inline fun noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver> ) = on(markerFactory, initialFilter, subcontextUpdatesFilter, scenarioReceiver) { when (it) { - is BaseEditMessageUpdate -> (it.editMessageUpdateOrNull() ?.data ?.withContent()) + is BaseEditMessageUpdate -> (it.data.withContent()) else -> null } ?.let(::listOfNotNull) } diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EventTriggers.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EventTriggers.kt index 06662e8a59..645ac7effb 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EventTriggers.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/EventTriggers.kt @@ -23,6 +23,9 @@ import dev.inmo.tgbotapi.types.message.PrivateEventMessage import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage import dev.inmo.tgbotapi.types.message.abstracts.SupergroupEventMessage import dev.inmo.tgbotapi.types.message.payments.SuccessfulPaymentEvent +import dev.inmo.tgbotapi.types.request.ChatShared +import dev.inmo.tgbotapi.types.request.ChatSharedRequest +import dev.inmo.tgbotapi.types.request.UserShared import dev.inmo.tgbotapi.types.update.abstracts.Update internal suspend inline fun BC.onEvent( @@ -657,3 +660,65 @@ suspend fun BC.onWriteAccessAllowed( markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, scenarioReceiver: CustomBehaviourContextAndTypeReceiver> ) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +/** + * @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call + * @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example, + * this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage]. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times] + * to combinate several filters + * @param markerFactory Will be used to identify different "stream". [scenarioReceiver] will be called synchronously + * in one "stream". Output of [markerFactory] will be used as a key for "stream" + * @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that + * data + */ +suspend fun BC.onChatSharedRequest( + initialFilter: SimpleFilter>? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = MessageFilterByChat, + markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + +/** + * @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call + * @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example, + * this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage]. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times] + * to combinate several filters + * @param markerFactory Will be used to identify different "stream". [scenarioReceiver] will be called synchronously + * in one "stream". Output of [markerFactory] will be used as a key for "stream" + * @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that + * data + */ +suspend fun BC.onUserShared( + initialFilter: SimpleFilter>? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = MessageFilterByChat, + markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) + + + +/** + * @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call + * @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example, + * this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage]. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own. + * Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times] + * to combinate several filters + * @param markerFactory Will be used to identify different "stream". [scenarioReceiver] will be called synchronously + * in one "stream". Output of [markerFactory] will be used as a key for "stream" + * @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that + * data + */ +suspend fun BC.onChatShared( + initialFilter: SimpleFilter>? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver, Update>? = MessageFilterByChat, + markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, + scenarioReceiver: CustomBehaviourContextAndTypeReceiver> +) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver) 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 fd27601da9..4dada53823 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 @@ -261,6 +261,19 @@ const val iconColorField = "icon_color" const val requestContactField = "request_contact" const val requestLocationField = "request_location" const val requestPollField = "request_poll" +const val requestUserField = "request_user" +const val requestChatField = "request_chat" +const val requestIdField = "request_id" + +const val userIsBotField = "user_is_bot" +const val userIsPremiumField = "user_is_premium" +const val chatIsChannelField = "chat_is_channel" +const val chatIsForumField = "chat_is_forum" +const val chatHasUsernameField = "chat_has_username" +const val chatIsCreatedField = "chat_is_created" +const val userAdministratorRightsField = "user_administrator_rights" +const val botAdministratorRightsField = "bot_administrator_rights" +const val botIsMemberField = "bot_is_member" const val fileNameField = "file_name" const val mimeTypeField = "mime_type" @@ -381,6 +394,7 @@ const val latitudeField = "latitude" const val longitudeField = "longitude" const val headingField = "heading" const val fromField = "from" +const val userChatIdField = "user_chat_id" const val userField = "user" const val dateField = "date" const val chatField = "chat" 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 4226bc94d2..64c326637d 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 @@ -94,6 +94,42 @@ data class RequestPollKeyboardButton( 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() @@ -124,6 +160,20 @@ object KeyboardButtonSerializer : KSerializer { 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 @@ -142,6 +192,8 @@ object KeyboardButtonSerializer : KSerializer { 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)) } } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonRequestChat.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonRequestChat.kt new file mode 100644 index 0000000000..fccb2c64ce --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonRequestChat.kt @@ -0,0 +1,78 @@ +package dev.inmo.tgbotapi.types.buttons + +import dev.inmo.tgbotapi.types.botAdministratorRightsField +import dev.inmo.tgbotapi.types.botIsMemberField +import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRights +import dev.inmo.tgbotapi.types.chatHasUsernameField +import dev.inmo.tgbotapi.types.chatIsChannelField +import dev.inmo.tgbotapi.types.chatIsCreatedField +import dev.inmo.tgbotapi.types.chatIsForumField +import dev.inmo.tgbotapi.types.request.RequestId +import dev.inmo.tgbotapi.types.requestIdField +import dev.inmo.tgbotapi.types.userAdministratorRightsField +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * @see Channel + * @see Group + */ +@Serializable +data class KeyboardButtonRequestChat( + @SerialName(requestIdField) + val requestId: RequestId, + @SerialName(chatIsChannelField) + val isChannel: Boolean? = null, + @SerialName(chatIsForumField) + val isForum: Boolean? = null, + @SerialName(chatHasUsernameField) + val isPublic: Boolean? = null, + @SerialName(chatIsCreatedField) + val isOwnedBy: Boolean? = null, + @SerialName(userAdministratorRightsField) + val userRightsInChat: ChatAdministratorRights? = null, + @SerialName(botAdministratorRightsField) + val botRightsInChat: ChatAdministratorRights? = null, + @SerialName(botIsMemberField) + val botIsMember: Boolean? = null +) { + companion object { + fun Channel( + requestId: RequestId, + isPublic: Boolean? = null, + isOwnedBy: Boolean? = null, + userRightsInChat: ChatAdministratorRights? = null, + botRightsInChat: ChatAdministratorRights? = null, + botIsMember: Boolean? = null + ) = KeyboardButtonRequestChat( + requestId = requestId, + isChannel = true, + isForum = null, + isPublic = isPublic, + isOwnedBy = isOwnedBy, + userRightsInChat = userRightsInChat, + botRightsInChat = botRightsInChat, + botIsMember = botIsMember + ) + + fun Group( + requestId: RequestId, + isForum: Boolean? = null, + isPublic: Boolean? = null, + isOwnedBy: Boolean? = null, + userRightsInChat: ChatAdministratorRights? = null, + botRightsInChat: ChatAdministratorRights? = null, + botIsMember: Boolean? = null + ) = KeyboardButtonRequestChat( + requestId = requestId, + isChannel = false, + isForum = isForum, + isPublic = isPublic, + isOwnedBy = isOwnedBy, + userRightsInChat = userRightsInChat, + botRightsInChat = botRightsInChat, + botIsMember = botIsMember + ) + } +} + diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonRequestUser.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonRequestUser.kt new file mode 100644 index 0000000000..ff7483b011 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/KeyboardButtonRequestUser.kt @@ -0,0 +1,89 @@ +package dev.inmo.tgbotapi.types.buttons + +import dev.inmo.tgbotapi.types.request.RequestId +import dev.inmo.tgbotapi.types.requestIdField +import dev.inmo.tgbotapi.types.userIsBotField +import dev.inmo.tgbotapi.types.userIsPremiumField +import kotlinx.serialization.EncodeDefault +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.Serializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@Serializable(KeyboardButtonRequestUser.Companion::class) +sealed interface KeyboardButtonRequestUser { + val requestId: RequestId + val isBot: Boolean? + + @Serializable + data class Any( + @SerialName(requestIdField) + override val requestId: RequestId + ) : KeyboardButtonRequestUser { + @SerialName(userIsBotField) + @EncodeDefault + override val isBot: Boolean? = null + } + + @Serializable + data class Common( + @SerialName(requestIdField) + override val requestId: RequestId, + @SerialName(userIsPremiumField) + val isPremium: Boolean? = null + ) : KeyboardButtonRequestUser { + @SerialName(userIsBotField) + @EncodeDefault + override val isBot: Boolean = false + } + + @Serializable + data class Bot( + @SerialName(requestIdField) + override val requestId: RequestId + ) : KeyboardButtonRequestUser { + @SerialName(userIsBotField) + @EncodeDefault + override val isBot: Boolean = true + } + + @Serializer(KeyboardButtonRequestUser::class) + companion object : KSerializer { + @Serializable + private data class Surrogate( + @SerialName(requestIdField) + val requestId: RequestId, + @SerialName(userIsBotField) + val userIsBot: Boolean? = null, + @SerialName(userIsPremiumField) + val userIsPremium: Boolean? = null + ) + private val realSerializer = Surrogate.serializer() + + override val descriptor: SerialDescriptor = realSerializer.descriptor + + override fun deserialize(decoder: Decoder): KeyboardButtonRequestUser { + val surrogate = realSerializer.deserialize(decoder) + + return when (surrogate.userIsBot) { + true -> Bot(surrogate.requestId) + false -> Common(surrogate.requestId, surrogate.userIsPremium) + null -> Any(surrogate.requestId) + } + } + + override fun serialize(encoder: Encoder, value: KeyboardButtonRequestUser) { + realSerializer.serialize( + encoder, + Surrogate( + value.requestId, + value.isBot, + (value as? Common) ?.isPremium + ) + ) + } + } +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/reply/ReplyKeyboardButtonsShortcuts.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/reply/ReplyKeyboardButtonsShortcuts.kt index d87ed524f4..434faf7723 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/reply/ReplyKeyboardButtonsShortcuts.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/buttons/reply/ReplyKeyboardButtonsShortcuts.kt @@ -2,6 +2,8 @@ package dev.inmo.tgbotapi.types.buttons.reply import dev.inmo.tgbotapi.types.buttons.* import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.* +import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRights +import dev.inmo.tgbotapi.types.request.RequestId import dev.inmo.tgbotapi.types.webapps.WebAppInfo @@ -67,3 +69,161 @@ inline fun webAppReplyButton( text: String, url: String ) = webAppReplyButton(text, WebAppInfo(url)) + + +/** + * Creates and put [RequestUserKeyboardButton] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun requestUserReplyButton( + text: String, + requestUser: KeyboardButtonRequestUser +) = RequestUserKeyboardButton( + text, + requestUser +) + +/** + * Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Bot] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun requestBotReplyButton( + text: String, + requestId: RequestId +) = requestUserReplyButton( + text, + KeyboardButtonRequestUser.Bot(requestId) +) + +/** + * Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Common] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun requestUserReplyButton( + text: String, + requestId: RequestId, + premiumUser: Boolean? = null +) = requestUserReplyButton( + text, + KeyboardButtonRequestUser.Common(requestId, premiumUser) +) + +/** + * Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Any] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun requestUserOrBotReplyButton( + text: String, + requestId: RequestId +) = requestUserReplyButton( + text, + KeyboardButtonRequestUser.Any(requestId) +) + + +/** + * Creates and put [RequestChatKeyboardButton] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun requestChatReplyButton( + text: String, + requestChat: KeyboardButtonRequestChat +) = RequestChatKeyboardButton( + text, + requestChat +) + +/** + * Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun requestChatReplyButton( + text: String, + requestId: RequestId, + isChannel: Boolean? = null, + isForum: Boolean? = null, + isPublic: Boolean? = null, + isOwnedBy: Boolean? = null, + userRightsInChat: ChatAdministratorRights? = null, + botRightsInChat: ChatAdministratorRights? = null, + botIsMember: Boolean = false +) = requestChatReplyButton( + text, + KeyboardButtonRequestChat( + requestId = requestId, + isChannel = isChannel, + isForum = isForum, + isPublic = isPublic, + isOwnedBy = isOwnedBy, + userRightsInChat = userRightsInChat, + botRightsInChat = botRightsInChat, + botIsMember = botIsMember + ) +) + +/** + * Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat.Channel] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun requestChannelReplyButton( + text: String, + requestId: RequestId, + isPublic: Boolean? = null, + isOwnedBy: Boolean? = null, + userRightsInChat: ChatAdministratorRights? = null, + botRightsInChat: ChatAdministratorRights? = null, + botIsMember: Boolean = false +) = requestChatReplyButton( + text, + KeyboardButtonRequestChat.Channel( + requestId = requestId, + isPublic = isPublic, + isOwnedBy = isOwnedBy, + userRightsInChat = userRightsInChat, + botRightsInChat = botRightsInChat, + botIsMember = botIsMember + ) +) + + +/** + * Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat.Group] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun requestChannelReplyButton( + text: String, + requestId: RequestId, + isForum: Boolean? = null, + isPublic: Boolean? = null, + isOwnedBy: Boolean? = null, + userRightsInChat: ChatAdministratorRights? = null, + botRightsInChat: ChatAdministratorRights? = null, + botIsMember: Boolean? = null +) = requestChatReplyButton( + text, + KeyboardButtonRequestChat.Group( + requestId = requestId, + isForum = isForum, + isPublic = isPublic, + isOwnedBy = isOwnedBy, + userRightsInChat = userRightsInChat, + botRightsInChat = botRightsInChat, + botIsMember = botIsMember + ) +) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ChatJoinRequest.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ChatJoinRequest.kt index 738e95adc6..90a00b33f7 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ChatJoinRequest.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ChatJoinRequest.kt @@ -15,6 +15,8 @@ data class ChatJoinRequest( val chat: PublicChat, @SerialName(fromField) override val from: User, + @SerialName(userChatIdField) + val userChatId: UserId, @SerialName(dateField) val date: TelegramDate, @SerialName(inviteLinkField) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/RawMessage.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/RawMessage.kt index c5c6277daa..684b74757f 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/RawMessage.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/RawMessage.kt @@ -29,6 +29,8 @@ import dev.inmo.tgbotapi.types.passport.PassportData import dev.inmo.tgbotapi.types.payments.Invoice import dev.inmo.tgbotapi.types.payments.SuccessfulPayment import dev.inmo.tgbotapi.types.polls.Poll +import dev.inmo.tgbotapi.types.request.ChatShared +import dev.inmo.tgbotapi.types.request.UserShared import dev.inmo.tgbotapi.types.venue.Venue import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -93,6 +95,9 @@ internal data class RawMessage( private val dice: Dice? = null, private val successful_payment: SuccessfulPayment? = null, + private val user_shared: UserShared? = null, + private val chat_shared: ChatShared? = null, + // Voice Chat Service Messages private val video_chat_scheduled: VideoChatScheduled? = null, private val video_chat_started: VideoChatStarted? = null, @@ -249,6 +254,8 @@ internal data class RawMessage( successful_payment != null -> SuccessfulPaymentEvent(successful_payment) connected_website != null -> UserLoggedIn(connected_website) web_app_data != null -> web_app_data + user_shared != null -> user_shared + chat_shared != null -> chat_shared else -> null } } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/ChatShared.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/ChatShared.kt new file mode 100644 index 0000000000..f222e5b380 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/ChatShared.kt @@ -0,0 +1,17 @@ +package dev.inmo.tgbotapi.types.request + +import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.ChatIdentifier +import dev.inmo.tgbotapi.types.UserId +import dev.inmo.tgbotapi.types.chatIdField +import dev.inmo.tgbotapi.types.requestIdField +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ChatShared( + @SerialName(requestIdField) + override val requestId: RequestId, + @SerialName(chatIdField) + override val chatId: ChatId +) : ChatSharedRequest diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/ChatSharedRequest.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/ChatSharedRequest.kt new file mode 100644 index 0000000000..5dbe702013 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/ChatSharedRequest.kt @@ -0,0 +1,8 @@ +package dev.inmo.tgbotapi.types.request + +import dev.inmo.tgbotapi.types.ChatIdentifier +import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.PrivateEvent + +sealed interface ChatSharedRequest : RequestResponse, PrivateEvent { + val chatId: ChatIdentifier +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/RequestId.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/RequestId.kt new file mode 100644 index 0000000000..5263de2366 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/RequestId.kt @@ -0,0 +1,15 @@ +package dev.inmo.tgbotapi.types.request + +import kotlinx.serialization.Serializable +import kotlin.jvm.JvmInline +import kotlin.random.Random + +@Serializable +@JvmInline +value class RequestId( + val float: Int +) { + companion object { + fun random() = RequestId(Random.nextInt()) + } +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/RequestResponse.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/RequestResponse.kt new file mode 100644 index 0000000000..2c4c846942 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/RequestResponse.kt @@ -0,0 +1,5 @@ +package dev.inmo.tgbotapi.types.request + +sealed interface RequestResponse { + val requestId: RequestId +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/UserShared.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/UserShared.kt new file mode 100644 index 0000000000..74b6f55667 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/request/UserShared.kt @@ -0,0 +1,19 @@ +package dev.inmo.tgbotapi.types.request + +import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.UserId +import dev.inmo.tgbotapi.types.requestIdField +import dev.inmo.tgbotapi.types.userIdField +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UserShared( + @SerialName(requestIdField) + override val requestId: RequestId, + @SerialName(userIdField) + val userId: UserId +) : ChatSharedRequest { + override val chatId: ChatId + get() = userId +} diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCastsNew.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCastsNew.kt index 500b28a94b..ceb2d6ba00 100644 --- a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCastsNew.kt +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCastsNew.kt @@ -433,6 +433,9 @@ import dev.inmo.tgbotapi.types.queries.callback.MessageCallbackQuery import dev.inmo.tgbotapi.types.queries.callback.MessageDataCallbackQuery import dev.inmo.tgbotapi.types.queries.callback.MessageGameShortNameCallbackQuery import dev.inmo.tgbotapi.types.queries.callback.UnknownCallbackQueryType +import dev.inmo.tgbotapi.types.request.ChatShared +import dev.inmo.tgbotapi.types.request.ChatSharedRequest +import dev.inmo.tgbotapi.types.request.UserShared import dev.inmo.tgbotapi.types.update.CallbackQueryUpdate import dev.inmo.tgbotapi.types.update.ChannelPostUpdate import dev.inmo.tgbotapi.types.update.ChatJoinRequestUpdate @@ -3031,6 +3034,33 @@ public inline fun ChatEvent.successfulPaymentEventOrThrow(): SuccessfulPaymentEv public inline fun ChatEvent.ifSuccessfulPaymentEvent(block: (SuccessfulPaymentEvent) -> T): T? = successfulPaymentEventOrNull() ?.let(block) +public inline fun ChatEvent.chatSharedOrNull(): ChatShared? = this as? + dev.inmo.tgbotapi.types.request.ChatShared + +public inline fun ChatEvent.chatSharedOrThrow(): ChatShared = this as + dev.inmo.tgbotapi.types.request.ChatShared + +public inline fun ChatEvent.ifChatShared(block: (ChatShared) -> T): T? = chatSharedOrNull() + ?.let(block) + +public inline fun ChatEvent.chatSharedRequestOrNull(): ChatSharedRequest? = this as? + dev.inmo.tgbotapi.types.request.ChatSharedRequest + +public inline fun ChatEvent.chatSharedRequestOrThrow(): ChatSharedRequest = this as + dev.inmo.tgbotapi.types.request.ChatSharedRequest + +public inline fun ChatEvent.ifChatSharedRequest(block: (ChatSharedRequest) -> T): T? = + chatSharedRequestOrNull() ?.let(block) + +public inline fun ChatEvent.userSharedOrNull(): UserShared? = this as? + dev.inmo.tgbotapi.types.request.UserShared + +public inline fun ChatEvent.userSharedOrThrow(): UserShared = this as + dev.inmo.tgbotapi.types.request.UserShared + +public inline fun ChatEvent.ifUserShared(block: (UserShared) -> T): T? = userSharedOrNull() + ?.let(block) + public inline fun ForwardInfo.byAnonymousOrNull(): ForwardInfo.ByAnonymous? = this as? dev.inmo.tgbotapi.types.message.ForwardInfo.ByAnonymous diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/buttons/ReplyKeyboardBuilder.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/buttons/ReplyKeyboardBuilder.kt index 9bd0909aa1..22acc0e117 100644 --- a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/buttons/ReplyKeyboardBuilder.kt +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/buttons/ReplyKeyboardBuilder.kt @@ -1,6 +1,10 @@ package dev.inmo.tgbotapi.extensions.utils.types.buttons import dev.inmo.tgbotapi.types.buttons.* +import dev.inmo.tgbotapi.types.buttons.reply.requestChatReplyButton +import dev.inmo.tgbotapi.types.buttons.reply.requestUserReplyButton +import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRights +import dev.inmo.tgbotapi.types.request.RequestId import dev.inmo.tgbotapi.types.webapps.WebAppInfo import dev.inmo.tgbotapi.utils.* @@ -138,3 +142,164 @@ inline fun ReplyKeyboardRowBuilder.webAppButton( text: String, url: String ) = webAppButton(text, WebAppInfo(url)) + + +/** + * Creates and put [RequestUserKeyboardButton] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun ReplyKeyboardRowBuilder.requestUserButton( + text: String, + requestUser: KeyboardButtonRequestUser +) = add( + requestUserReplyButton( + text, + requestUser + ) +) + +/** + * Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Bot] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun ReplyKeyboardRowBuilder.requestBotButton( + text: String, + requestId: RequestId +) = requestUserButton( + text, + KeyboardButtonRequestUser.Bot(requestId) +) + +/** + * Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Common] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun ReplyKeyboardRowBuilder.requestUserButton( + text: String, + requestId: RequestId, + premiumUser: Boolean? = null +) = requestUserButton( + text, + KeyboardButtonRequestUser.Common(requestId, premiumUser) +) + +/** + * Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Any] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun ReplyKeyboardRowBuilder.requestUserOrBotButton( + text: String, + requestId: RequestId +) = requestUserButton( + text, + KeyboardButtonRequestUser.Any(requestId) +) + + +/** + * Creates and put [RequestChatKeyboardButton] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun ReplyKeyboardRowBuilder.requestChatButton( + text: String, + requestChat: KeyboardButtonRequestChat +) = add( + requestChatReplyButton( + text, + requestChat + ) +) + +/** + * Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun ReplyKeyboardRowBuilder.requestChatButton( + text: String, + requestId: RequestId, + isChannel: Boolean? = null, + isForum: Boolean? = null, + isPublic: Boolean? = null, + isOwnedBy: Boolean? = null, + userRightsInChat: ChatAdministratorRights? = null, + botRightsInChat: ChatAdministratorRights? = null, + botIsMember: Boolean? = null +) = requestChatButton( + text, + KeyboardButtonRequestChat( + requestId = requestId, + isChannel = isChannel, + isForum = isForum, + isPublic = isPublic, + isOwnedBy = isOwnedBy, + userRightsInChat = userRightsInChat, + botRightsInChat = botRightsInChat, + botIsMember = botIsMember + ) +) + +/** + * Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat.Channel] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun ReplyKeyboardRowBuilder.requestChannelButton( + text: String, + requestId: RequestId, + isPublic: Boolean? = null, + isOwnedBy: Boolean? = null, + userRightsInChat: ChatAdministratorRights? = null, + botRightsInChat: ChatAdministratorRights? = null, + botIsMember: Boolean? = null +) = requestChatButton( + text, + KeyboardButtonRequestChat.Channel( + requestId = requestId, + isPublic = isPublic, + isOwnedBy = isOwnedBy, + userRightsInChat = userRightsInChat, + botRightsInChat = botRightsInChat, + botIsMember = botIsMember + ) +) + +/** + * Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat.Group] + * + * @see replyKeyboard + * @see ReplyKeyboardBuilder.row + */ +inline fun ReplyKeyboardRowBuilder.requestGroupButton( + text: String, + requestId: RequestId, + isForum: Boolean? = null, + isPublic: Boolean? = null, + isOwnedBy: Boolean? = null, + userRightsInChat: ChatAdministratorRights? = null, + botRightsInChat: ChatAdministratorRights? = null, + botIsMember: Boolean? = null +) = requestChatButton( + text, + KeyboardButtonRequestChat.Group( + requestId = requestId, + isForum = isForum, + isPublic = isPublic, + isOwnedBy = isOwnedBy, + userRightsInChat = userRightsInChat, + botRightsInChat = botRightsInChat, + botIsMember = botIsMember + ) +)