feat(api)!: use direct message threads for drafts

BREAKING CHANGE: draft APIs replace threadId/MessageThreadId with
directMessageThreadId/DirectMessageThreadId and serialize
direct_messages_topic_id.
This commit is contained in:
InsanusMokrassar committed 2026-09-01 22:42:58 +06:00
1 parent 3ca3e571b5
commit b1304ef10d
11 files changed
+288 -133

No files matched your search

@@ -9,23 +9,23 @@ import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.types.message.parseModeField
import dev.inmo.tgbotapi.types.message.*
import dev.inmo.tgbotapi.types.message.RawMessageEntity
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializationStrategyClass
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.toRawMessageEntities
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
import dev.inmo.tgbotapi.utils.extensions.makeString
import dev.inmo.tgbotapi.utils.serializers.UnitFromBooleanSerializer
import dev.inmo.tgbotapi.utils.throwRangeError
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
/**
* Creates a plain-text draft for a direct messages topic.
*
* @param directMessageThreadId Direct messages topic identifier. Defaults from [chatId].
*/
fun SendMessageDraft(
chatId: IdChatIdentifier,
draftId: DraftId,
text: String,
parseMode: ParseMode? = null,
threadId: MessageThreadId? = chatId.threadId,
directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
canStop: Boolean? = null,
keepOnStop: Boolean? = null
) = SendMessageDraft(
@@ -34,16 +34,21 @@ fun SendMessageDraft(
text = text,
parseMode = parseMode,
rawEntities = null,
threadId = threadId,
directMessageThreadId = directMessageThreadId,
canStop = canStop,
keepOnStop = keepOnStop
)
/**
* Creates an entity-formatted draft for a direct messages topic.
*
* @param directMessageThreadId Direct messages topic identifier. Defaults from [chatId].
*/
fun SendMessageDraft(
chatId: IdChatIdentifier,
draftId: DraftId,
entities: TextSourcesList,
threadId: MessageThreadId? = chatId.threadId,
directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
canStop: Boolean? = null,
keepOnStop: Boolean? = null
) = SendMessageDraft(
@@ -52,11 +57,16 @@ fun SendMessageDraft(
text = entities.makeString(),
parseMode = null,
rawEntities = entities.toRawMessageEntities(),
threadId = threadId,
directMessageThreadId = directMessageThreadId,
canStop = canStop,
keepOnStop = keepOnStop
)
/**
* Streams a partial text message draft to a direct messages topic.
*
* @param directMessageThreadId Direct messages topic identifier. Defaults from [chatId].
*/
@ConsistentCopyVisibility
@Serializable
data class SendMessageDraft internal constructor(
@@ -71,16 +81,16 @@ data class SendMessageDraft internal constructor(
@SerialName(entitiesField)
private val rawEntities: List<RawMessageEntity>? = null,
@OptIn(ExperimentalSerializationApi::class)
@SerialName(messageThreadIdField)
@SerialName(directMessagesTopicIdField)
@EncodeDefault
override val threadId: MessageThreadId? = chatId.threadId,
override val directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
@SerialName(canStopField)
val canStop: Boolean? = null,
@SerialName(keepOnStopField)
val keepOnStop: Boolean? = null
) : SendChatMessageRequest<Unit>,
TextedOutput,
OptionallyMessageThreadRequest
OptionallyDirectMessageThreadRequest
{
override val textSources: TextSourcesList? by lazy {
rawEntities ?.asTextSources(text)
@@ -1,13 +1,13 @@
package dev.inmo.tgbotapi.requests.send
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.DirectMessageThreadId
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.canStopField
import dev.inmo.tgbotapi.types.chatIdField
import dev.inmo.tgbotapi.types.directMessagesTopicIdField
import dev.inmo.tgbotapi.types.draftIdField
import dev.inmo.tgbotapi.types.keepOnStopField
import dev.inmo.tgbotapi.types.messageThreadIdField
import dev.inmo.tgbotapi.types.richMessageField
import dev.inmo.tgbotapi.types.rich.InputRichMessage
import dev.inmo.tgbotapi.types.rich.multipartFiles
@@ -22,7 +22,8 @@ import kotlinx.serialization.SerializationStrategy
* ephemeral and acts as a temporary 30-second preview - once the output is finalized, [SendRichMessage] must be called
* with the complete message to persist it in the user's chat.
*
* @param chatId Numeric target chat identifier. Identifiers carrying a thread supply the default [threadId].
* @param chatId Numeric target chat identifier.
* @param directMessageThreadId Direct messages topic identifier. Defaults from [chatId].
* @see <a href="https://core.telegram.org/bots/api#sendrichmessagedraft">sendRichMessageDraft</a>
*/
@Serializable
@@ -36,8 +37,8 @@ data class SendRichMessageDraft(
val draftId: Long,
@SerialName(richMessageField)
val richMessage: InputRichMessage,
@SerialName(messageThreadIdField)
val threadId: MessageThreadId? = chatId.threadId,
@SerialName(directMessagesTopicIdField)
val directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
@SerialName(canStopField)
val canStop: Boolean? = null,
@SerialName(keepOnStopField)
@@ -38,15 +38,23 @@ class GeneralBotApi103Test {
}
@Test
fun `rich draft accepts thread-bearing chat identifiers`() {
val chatId = ChatIdWithThreadId(RawChatId(3L), MessageThreadId(4L))
fun `drafts accept direct-message-thread chat identifiers`() {
val chatId = ChatIdWithChannelDirectMessageThreadId(RawChatId(3L), DirectMessageThreadId(4L))
val messageDraft = SendMessageDraft(chatId, DraftId(5L), "draft")
val richDraft = SendRichMessageDraft(chatId, 5L, InputRichMessageHTML("draft"))
val messageDraftJson = json.encodeToJsonElement(SendMessageDraft.serializer(), messageDraft).jsonObject
val richDraftJson = json.encodeToJsonElement(SendRichMessageDraft.serializer(), richDraft).jsonObject
assertEquals(chatId, messageDraft.chatId)
assertEquals(chatId, richDraft.chatId)
assertEquals(MessageThreadId(4L), richDraft.threadId)
assertEquals(DirectMessageThreadId(4L), messageDraft.directMessageThreadId)
assertEquals(DirectMessageThreadId(4L), richDraft.directMessageThreadId)
assertEquals("3", messageDraftJson[chatIdField].toString())
assertEquals("3", richDraftJson[chatIdField].toString())
assertEquals("4", richDraftJson[messageThreadIdField].toString())
assertEquals("4", messageDraftJson[directMessagesTopicIdField].toString())
assertEquals("4", richDraftJson[directMessagesTopicIdField].toString())
assertEquals(null, messageDraftJson[messageThreadIdField])
assertEquals(null, richDraftJson[messageThreadIdField])
}
@Test