diff --git a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/get/GetChatMenuButton.kt b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/get/GetChatMenuButton.kt new file mode 100644 index 0000000000..ef8404b98f --- /dev/null +++ b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/get/GetChatMenuButton.kt @@ -0,0 +1,15 @@ +package dev.inmo.tgbotapi.extensions.api.chat.get + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.chat.get.GetChatMenuButton +import dev.inmo.tgbotapi.requests.chat.modify.* +import dev.inmo.tgbotapi.types.* +import dev.inmo.tgbotapi.types.chat.abstracts.PrivateChat + +suspend fun TelegramBot.getChatMenuButton( + chatId: ChatId +) = execute(GetChatMenuButton(chatId)) + +suspend fun TelegramBot.getChatMenuButton( + chat: PrivateChat +) = getChatMenuButton(chat.id) diff --git a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/get/GetDefaultChatMenuButton.kt b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/get/GetDefaultChatMenuButton.kt new file mode 100644 index 0000000000..e03a4f88e0 --- /dev/null +++ b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/get/GetDefaultChatMenuButton.kt @@ -0,0 +1,8 @@ +package dev.inmo.tgbotapi.extensions.api.chat.get + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.chat.get.GetDefaultChatMenuButton +import dev.inmo.tgbotapi.requests.chat.modify.SetDefaultChatMenuButton +import dev.inmo.tgbotapi.types.MenuButton + +suspend fun TelegramBot.getDefaultChatMenuButton() = execute(GetDefaultChatMenuButton) diff --git a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/modify/SetChatMenuButton.kt b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/modify/SetChatMenuButton.kt new file mode 100644 index 0000000000..ed06222c50 --- /dev/null +++ b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/modify/SetChatMenuButton.kt @@ -0,0 +1,16 @@ +package dev.inmo.tgbotapi.extensions.api.chat.modify + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.chat.modify.* +import dev.inmo.tgbotapi.types.* +import dev.inmo.tgbotapi.types.chat.abstracts.PrivateChat + +suspend fun TelegramBot.setChatMenuButton( + chatId: ChatId, + menuButton: MenuButton +) = execute(SetChatMenuButton(chatId, menuButton)) + +suspend fun TelegramBot.setChatMenuButton( + chat: PrivateChat, + menuButton: MenuButton +) = setChatMenuButton(chat.id, menuButton) diff --git a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/modify/SetDefaultChatMenuButton.kt b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/modify/SetDefaultChatMenuButton.kt new file mode 100644 index 0000000000..1239825d00 --- /dev/null +++ b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/chat/modify/SetDefaultChatMenuButton.kt @@ -0,0 +1,9 @@ +package dev.inmo.tgbotapi.extensions.api.chat.modify + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.chat.modify.SetDefaultChatMenuButton +import dev.inmo.tgbotapi.types.MenuButton + +suspend fun TelegramBot.setDefaultChatMenuButton( + menuButton: MenuButton +) = execute(SetDefaultChatMenuButton(menuButton)) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/CommonAbstracts/types/ChatRequest.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/CommonAbstracts/types/ChatRequest.kt index bdf3ba4e1b..f314edaceb 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/CommonAbstracts/types/ChatRequest.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/CommonAbstracts/types/ChatRequest.kt @@ -2,6 +2,6 @@ package dev.inmo.tgbotapi.CommonAbstracts.types import dev.inmo.tgbotapi.types.ChatIdentifier -interface ChatRequest { - val chatId: ChatIdentifier -} \ No newline at end of file +interface ChatRequest : OptionalChatRequest { + override val chatId: ChatIdentifier +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/CommonAbstracts/types/OptionalChatRequest.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/CommonAbstracts/types/OptionalChatRequest.kt new file mode 100644 index 0000000000..42bf118d28 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/CommonAbstracts/types/OptionalChatRequest.kt @@ -0,0 +1,7 @@ +package dev.inmo.tgbotapi.CommonAbstracts.types + +import dev.inmo.tgbotapi.types.ChatIdentifier + +interface OptionalChatRequest { + val chatId: ChatIdentifier? +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetChatMenuButton.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetChatMenuButton.kt new file mode 100644 index 0000000000..b4740a353c --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetChatMenuButton.kt @@ -0,0 +1,21 @@ +package dev.inmo.tgbotapi.requests.chat.get + +import dev.inmo.tgbotapi.CommonAbstracts.types.ChatRequest +import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest +import dev.inmo.tgbotapi.types.* +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer + +@Serializable +data class GetChatMenuButton( + @SerialName(chatIdField) + override val chatId: ChatIdentifier +) : ChatRequest, SimpleRequest { + override val requestSerializer: SerializationStrategy<*> + get() = serializer() + + override fun method(): String = GetDefaultChatMenuButton.method() + + override val resultDeserializer: DeserializationStrategy + get() = MenuButtonSerializer +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetDefaultChatMenuButton.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetDefaultChatMenuButton.kt new file mode 100644 index 0000000000..0c942acece --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetDefaultChatMenuButton.kt @@ -0,0 +1,21 @@ +package dev.inmo.tgbotapi.requests.chat.get + +import dev.inmo.tgbotapi.CommonAbstracts.types.ChatRequest +import dev.inmo.tgbotapi.CommonAbstracts.types.OptionalChatRequest +import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest +import dev.inmo.tgbotapi.types.* +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer + +@Serializable +object GetDefaultChatMenuButton : OptionalChatRequest, SimpleRequest { + override val chatId: ChatIdentifier? + get() = null + override val requestSerializer: SerializationStrategy<*> + get() = serializer() + + override fun method(): String = "getChatMenuButton" + + override val resultDeserializer: DeserializationStrategy + get() = MenuButtonSerializer +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/modify/SetChatMenuButton.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/modify/SetChatMenuButton.kt new file mode 100644 index 0000000000..6c8cc9dd3d --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/modify/SetChatMenuButton.kt @@ -0,0 +1,24 @@ +package dev.inmo.tgbotapi.requests.chat.modify + +import dev.inmo.tgbotapi.CommonAbstracts.types.ChatRequest +import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest +import dev.inmo.tgbotapi.types.* +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer + +@Serializable +data class SetChatMenuButton( + @SerialName(chatIdField) + override val chatId: ChatIdentifier, + @Serializable(MenuButtonSerializer::class) + @SerialName(menuButtonField) + val menuButton: MenuButton +) : ChatRequest, SimpleRequest { + override val requestSerializer: SerializationStrategy<*> + get() = serializer() + + override fun method(): String = SetDefaultChatMenuButton.method() + + override val resultDeserializer: DeserializationStrategy + get() = Boolean.serializer() +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/modify/SetDefaultChatMenuButton.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/modify/SetDefaultChatMenuButton.kt new file mode 100644 index 0000000000..7ce92e14ed --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/modify/SetDefaultChatMenuButton.kt @@ -0,0 +1,25 @@ +package dev.inmo.tgbotapi.requests.chat.modify + +import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest +import dev.inmo.tgbotapi.types.* +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer + +@Serializable +data class SetDefaultChatMenuButton( + @Serializable(MenuButtonSerializer::class) + @SerialName(menuButtonField) + val menuButton: MenuButton +) : SimpleRequest { + override val requestSerializer: SerializationStrategy<*> + get() = serializer() + + override fun method(): String = Companion.method() + + override val resultDeserializer: DeserializationStrategy + get() = Boolean.serializer() + + companion object { + fun method() = "setChatMenuButton" + } +} 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 e9a307d03c..3c69c82260 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 @@ -439,3 +439,5 @@ const val passportRegistrationField = "passport_registration" const val temporaryRegistrationField = "temporary_registration" const val buttonTextField = "button_text" +const val webAppField = "web_app" +const val menuButtonField = "menu_button" diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/MenuButton.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/MenuButton.kt new file mode 100644 index 0000000000..3ce057ce7c --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/MenuButton.kt @@ -0,0 +1,122 @@ +package dev.inmo.tgbotapi.types + +import dev.inmo.tgbotapi.types.webapps.WebAppInfo +import dev.inmo.tgbotapi.utils.RiskFeature +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.* + +@Serializable(MenuButtonSerializer::class) +sealed interface MenuButton { + @Required + val type: String + + @Serializable + object Commands : MenuButton { + @Required + override val type: String + get() = "commands" + } + + @Serializable + data class WebApp( + val text: String, + @SerialName(webAppField) + val webApp: WebAppInfo + ) : MenuButton { + @Required + override val type: String + get() = Companion.type + + companion object { + val type: String + get() = "web_app" + } + } + + @Serializable + object Default : MenuButton { + @Required + override val type: String + get() = "default" + } + + @Serializable + @RiskFeature + data class Unknown ( + override val type: String, + val rawJson: JsonElement + ) : MenuButton + + companion object { + fun serializer(): KSerializer = MenuButtonSerializer + } +} + +@Serializable +internal data class MenuButtonSurrogate( + val type: String, + val text: String? = null, + @SerialName(webAppField) + val webApp: WebAppInfo? = null, + val srcJsonElement: JsonElement? = null +) + +@Serializer(MenuButton::class) +object MenuButtonSerializer : KSerializer { + override val descriptor: SerialDescriptor + get() = MenuButtonSurrogate.serializer().descriptor + + override fun deserialize(decoder: Decoder): MenuButton { + val surrogate = if (decoder is JsonDecoder) { + val json = JsonElement.serializer().deserialize(decoder) + runCatching { + decoder.json.decodeFromJsonElement(MenuButtonSurrogate.serializer(), json) + }.onFailure { + return MenuButton.Unknown( + runCatching { json.jsonObject[typeField] ?.jsonPrimitive ?.content }.getOrNull() ?: "", + json + ) + }.getOrThrow().copy( + srcJsonElement = json + ) + } else { + MenuButtonSurrogate.serializer().deserialize(decoder) + } + + return when (surrogate.type) { + MenuButton.Commands.type -> MenuButton.Commands + MenuButton.Default.type -> MenuButton.Default + MenuButton.WebApp.type -> if (surrogate.text != null && surrogate.webApp != null) { + MenuButton.WebApp(surrogate.text, surrogate.webApp) + } else { + null + } + else -> null + } ?: MenuButton.Unknown( + surrogate.type, + surrogate.srcJsonElement ?: buildJsonObject { } + ) + } + + override fun serialize(encoder: Encoder, value: MenuButton) { + encoder.encodeSerializableValue( + MenuButtonSurrogate.serializer(), + when (value) { + MenuButton.Default, + MenuButton.Commands -> MenuButtonSurrogate(value.type) + is MenuButton.WebApp -> MenuButtonSurrogate(value.type, value.text, value.webApp) + is MenuButton.Unknown -> { + encoder.encodeSerializableValue( + JsonElement.serializer(), + value.rawJson + ) + return + } + } + ) + } + +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/webapps/WebAppInfo.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/webapps/WebAppInfo.kt new file mode 100644 index 0000000000..09ede53453 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/webapps/WebAppInfo.kt @@ -0,0 +1,11 @@ +package dev.inmo.tgbotapi.types.webapps + +import dev.inmo.tgbotapi.types.urlField +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class WebAppInfo( + @SerialName(urlField) + val url: String +)