diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetChat.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetChat.kt index 7e929880c9..1f4eca1db4 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetChat.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/chat/get/GetChat.kt @@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.requests.chat.get import dev.inmo.tgbotapi.abstracts.types.ChatRequest import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest +import dev.inmo.tgbotapi.types.BusinessChatId import dev.inmo.tgbotapi.types.ChatIdWithThreadId import dev.inmo.tgbotapi.types.ChatIdentifier import dev.inmo.tgbotapi.types.chat.ExtendedChatSerializer @@ -16,10 +17,10 @@ data class GetChat( ): ChatRequest, SimpleRequest { override fun method(): String = "getChat" @Transient - override val resultDeserializer: DeserializationStrategy = if (chatId is ChatIdWithThreadId) { - ExtendedChatSerializer.BasedOnForumThread(chatId.threadId) - } else { - ExtendedChatSerializer.Companion + override val resultDeserializer: DeserializationStrategy = when { + chatId is ChatIdWithThreadId -> ExtendedChatSerializer.BasedOnForumThread(chatId.threadId) + chatId is BusinessChatId -> ExtendedChatSerializer.BasedOnBusinessConnection(chatId.businessId) + else -> ExtendedChatSerializer.Companion } override val requestSerializer: SerializationStrategy<*> get() = serializer() diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/ChatIdentifier.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/ChatIdentifier.kt index 6886e18f1b..6a566ceb4b 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/ChatIdentifier.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/ChatIdentifier.kt @@ -1,6 +1,7 @@ package dev.inmo.tgbotapi.types import dev.inmo.micro_utils.common.Warning +import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.utils.RiskFeature import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded @@ -29,12 +30,17 @@ sealed interface IdChatIdentifier : ChatIdentifier { abstract val chatId: RawChatId val threadId: MessageThreadId? get() = null + val businessId: BusinessConnectionId? + get() = null companion object { operator fun invoke(chatId: RawChatId) = ChatId(chatId) operator fun invoke(chatId: RawChatId, threadId: MessageThreadId?) = threadId ?.let { ChatIdWithThreadId(chatId, threadId) } ?: ChatId(chatId) + operator fun invoke(chatId: RawChatId, businessConnectionId: BusinessConnectionId?) = businessConnectionId ?.let { + BusinessChatId(chatId, businessConnectionId) + } ?: ChatId(chatId) } } @@ -52,6 +58,16 @@ value class ChatIdWithThreadId(val chatIdWithThreadId: Pair) : IdChatIdentifier { + override val chatId: RawChatId + get() = chatIdWithBusinessConnectionId.first + override val businessId: BusinessConnectionId + get() = chatIdWithBusinessConnectionId.second + + constructor(chatId: RawChatId, businessConnectionId: BusinessConnectionId): this(chatId to businessConnectionId) +} val ChatIdentifier.threadId: MessageThreadId? get() = (this as? IdChatIdentifier) ?.threadId @@ -59,9 +75,11 @@ val ChatIdentifier.threadId: MessageThreadId? fun IdChatIdentifier.toChatId() = when (this) { is ChatId -> this is ChatIdWithThreadId -> ChatId(chatId) + is BusinessChatId -> ChatId(chatId) } fun IdChatIdentifier.toChatWithThreadId(threadId: MessageThreadId) = IdChatIdentifier(chatId, threadId) +fun IdChatIdentifier.toBusinessChatId(businessConnectionId: BusinessConnectionId) = IdChatIdentifier(chatId, businessConnectionId) /** * https://core.telegram.org/bots/api#formatting-options @@ -145,14 +163,22 @@ object FullChatIdentifierSerializer : KSerializer { ChatId(RawChatId(it)) } ?:let { val splitted = id.content.split("/") - if (splitted.size == 2) { - val (chatId, threadId) = splitted - ChatIdWithThreadId( - chatId.toLongOrNull() ?.let(::RawChatId) ?: return@let null, - threadId.toLongOrNull() ?.let(::MessageThreadId) ?: return@let null - ) - } else { - null + when (splitted.size) { + 2 -> { + val (chatId, threadId) = splitted + ChatIdWithThreadId( + chatId.toLongOrNull() ?.let(::RawChatId) ?: return@let null, + threadId.toLongOrNull() ?.let(::MessageThreadId) ?: return@let null + ) + } + 3 -> { + val (chatId, _, businessConnectionId) = splitted + BusinessChatId( + chatId.toLongOrNull() ?.let(::RawChatId) ?: return@let null, + businessConnectionId.let(::BusinessConnectionId) ?: return@let null + ) + } + else -> null } } ?: id.content.let { if (!it.startsWith("@")) { @@ -167,6 +193,7 @@ object FullChatIdentifierSerializer : KSerializer { when (value) { is ChatId -> encoder.encodeLong(value.chatId.long) is ChatIdWithThreadId -> encoder.encodeString("${value.chatId}/${value.threadId}") + is BusinessChatId -> encoder.encodeString("${value.chatId}//${value.businessId}") is Username -> encoder.encodeString(value.full) } } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/business_connection/BusinessConnectionId.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/business_connection/BusinessConnectionId.kt index c375dc27ba..75372d9e76 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/business_connection/BusinessConnectionId.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/business_connection/BusinessConnectionId.kt @@ -7,4 +7,8 @@ import kotlin.jvm.JvmInline @JvmInline value class BusinessConnectionId( val string: String -) +) { + override fun toString(): String { + return string + } +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Abstracts.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Abstracts.kt index e2e85645ef..0f78a666af 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Abstracts.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Abstracts.kt @@ -16,6 +16,12 @@ sealed interface PrivateChat : Chat, UsernameChat { val lastName: String } +@Serializable(ChatSerializer::class) +sealed interface BusinessChat : Chat { + override val id: BusinessChatId + val original: PrivateChat +} + @Serializable(ChatSerializer::class) sealed interface PublicChat : Chat { val title: String diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ChatSerializers.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ChatSerializers.kt index 7a3cd4ad63..e88bce2509 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ChatSerializers.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ChatSerializers.kt @@ -1,6 +1,7 @@ package dev.inmo.tgbotapi.types.chat import dev.inmo.tgbotapi.types.* +import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId import dev.inmo.tgbotapi.utils.RiskFeature import dev.inmo.tgbotapi.utils.nonstrictJsonFormat import kotlinx.serialization.* @@ -103,10 +104,17 @@ object PreviewChatSerializer : KSerializer { val type = decodedJson[typeField] ?.jsonPrimitive ?.content ?.asChatType ?: error("Field $typeField must be presented, but absent in $decodedJson") val isForum = decodedJson[isForumField] ?.jsonPrimitive ?.booleanOrNull == true + val isBusiness = decodedJson[chatIdField] ?.jsonPrimitive ?.contentOrNull ?.contains("//") == true return when (type) { ChatType.Sender -> formatter.decodeFromJsonElement(PrivateChatImpl.serializer(), decodedJson) - ChatType.Private -> formatter.decodeFromJsonElement(PrivateChatImpl.serializer(), decodedJson) + ChatType.Private -> { + if (isBusiness) { + formatter.decodeFromJsonElement(BusinessChatImpl.serializer(), decodedJson) + } else { + formatter.decodeFromJsonElement(PrivateChatImpl.serializer(), decodedJson) + } + } ChatType.Group -> formatter.decodeFromJsonElement(GroupChatImpl.serializer(), decodedJson) ChatType.Supergroup -> if (isForum) { formatter.decodeFromJsonElement(ForumChatImpl.serializer(), decodedJson) @@ -125,6 +133,7 @@ object PreviewChatSerializer : KSerializer { override fun serialize(encoder: Encoder, value: PreviewChat) { when (value) { is PrivateChatImpl -> PrivateChatImpl.serializer().serialize(encoder, value) + is BusinessChatImpl -> BusinessChatImpl.serializer().serialize(encoder, value) is GroupChatImpl -> GroupChatImpl.serializer().serialize(encoder, value) is SupergroupChatImpl -> SupergroupChatImpl.serializer().serialize(encoder, value) is ForumChatImpl -> ForumChatImpl.serializer().serialize(encoder, value) @@ -190,6 +199,19 @@ sealed class ExtendedChatSerializer : KSerializer { } } } + class BasedOnBusinessConnection(private val businessConnectionId: BusinessConnectionId) : ExtendedChatSerializer() { + override fun deserialize(decoder: Decoder): ExtendedChat { + return super.deserialize(decoder).let { + if (it is ExtendedPrivateChatImpl) { + it.copy( + id = (it.id as? BusinessChatId) ?: BusinessChatId(it.id.chatId, businessConnectionId) + ) + } else { + it + } + } + } + } companion object : ExtendedChatSerializer() } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ExtendedAbstracts.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ExtendedAbstracts.kt index 208b7ee986..1f7b7c313c 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ExtendedAbstracts.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/ExtendedAbstracts.kt @@ -93,3 +93,8 @@ sealed interface ExtendedForumChat : ExtendedSupergroupChat, ForumChat sealed interface ExtendedChatWithUsername : UsernameChat, ExtendedChat { val activeUsernames: List } + +@Serializable(ExtendedChatSerializer.Companion::class) +sealed interface ExtendedBusinessChat : BusinessChat, ExtendedChat { + override val original: ExtendedPrivateChat +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Impls.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Impls.kt index 70ce032703..be6419add7 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Impls.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Impls.kt @@ -30,6 +30,15 @@ data class PrivateChatImpl( override val lastName: String = "" ) : PreviewPrivateChat +@Serializable +@RiskFeature("This class is a subject of changes. It is better to use PrivateChat due") +data class BusinessChatImpl( + @SerialName(idField) + override val id: BusinessChatId, + @SerialName(firstNameField) + override val original: PreviewPrivateChat +) : PreviewBusinessChat + @Serializable @RiskFeature("This class is a subject of changes. It is better to use SupergroupChat due") data class SupergroupChatImpl( diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/PreviewAbstracts.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/PreviewAbstracts.kt index c7f89db815..ad8692bcd1 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/PreviewAbstracts.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/PreviewAbstracts.kt @@ -11,6 +11,9 @@ sealed interface PreviewUsernameChat : PreviewChat, UsernameChat @Serializable(PreviewChatSerializer::class) sealed interface PreviewPrivateChat : PreviewUsernameChat, PrivateChat +@Serializable(PreviewChatSerializer::class) +sealed interface PreviewBusinessChat : BusinessChat, PreviewChat + @Serializable(PreviewChatSerializer::class) sealed interface PreviewPublicChat : PreviewChat, PublicChat