1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2026-06-11 01:57:23 +00:00

Compare commits

...

4 Commits

Author SHA1 Message Date
71ccfc88cc start add SendChecklist 2025-07-07 16:25:21 +06:00
dd2923f92d complete base checklists? 2025-07-07 15:39:48 +06:00
ff78153591 small improvements 2025-07-07 15:18:22 +06:00
e2ce9cfebf preview version of inputs for tasks 2025-07-07 14:38:39 +06:00
9 changed files with 429 additions and 94 deletions

View File

@@ -27,3 +27,12 @@ interface TextedOutput : ParsableOutput, EntitiesOutput
interface TextedInput : TextedWithTextSources {
override val textSources: List<TextSource>
}
interface TitledInput : TextedInput {
val title: String
val titleTextSources: List<TextSource>
override val text: String
get() = title
override val textSources: List<TextSource>
get() = titleTextSources
}

View File

@@ -0,0 +1,75 @@
package dev.inmo.tgbotapi.requests.send
import dev.inmo.tgbotapi.abstracts.types.AllowPaidBroadcast
import dev.inmo.tgbotapi.abstracts.types.DisableNotification
import dev.inmo.tgbotapi.abstracts.types.OptionallyWithEffectId
import dev.inmo.tgbotapi.abstracts.types.ProtectContent
import dev.inmo.tgbotapi.abstracts.types.WithBusinessConnectionId
import dev.inmo.tgbotapi.abstracts.types.WithReplyMarkup
import dev.inmo.tgbotapi.abstracts.types.WithReplyParameters
import dev.inmo.tgbotapi.requests.send.abstracts.OptionallyMessageThreadRequest
import dev.inmo.tgbotapi.requests.send.abstracts.SendChatMessageRequest
import dev.inmo.tgbotapi.requests.send.abstracts.SendContentMessageRequest
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.checklists.Checklist
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializationStrategyClass
import dev.inmo.tgbotapi.types.message.content.ChecklistContent
import dev.inmo.tgbotapi.types.message.content.GameContent
import kotlinx.serialization.*
private val commonResultDeserializer: DeserializationStrategy<ContentMessage<ChecklistContent>>
= TelegramBotAPIMessageDeserializationStrategyClass()
@Serializable
data class SendChecklist (
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(checklistField)
val checklist: Checklist.Input,
@SerialName(businessConnectionIdField)
override val businessConnectionId: BusinessConnectionId,
@SerialName(disableNotificationField)
override val disableNotification: Boolean = false,
@SerialName(protectContentField)
override val protectContent: Boolean = false,
@SerialName(messageEffectIdField)
override val effectId: EffectId? = null,
@SerialName(replyParametersField)
override val replyParameters: ReplyParameters? = null,
@SerialName(replyMarkupField)
override val replyMarkup: KeyboardMarkup? = null
) : SendChatMessageRequest<ContentMessage<ChecklistContent>>,
WithReplyParameters,
DisableNotification,
ProtectContent,
OptionallyWithEffectId,
WithBusinessConnectionId,
WithReplyMarkup {
constructor(
chatId: BusinessChatId,
checklist: Checklist.Input,
disableNotification: Boolean = false,
protectContent: Boolean = false,
effectId: EffectId? = null,
replyParameters: ReplyParameters? = null,
replyMarkup: KeyboardMarkup? = null
) : this(
chatId = chatId,
checklist = checklist,
businessConnectionId = chatId.businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
effectId = effectId,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)
override fun method(): String = "sendChecklist"
override val resultDeserializer: DeserializationStrategy<ContentMessage<ChecklistContent>>
get() = commonResultDeserializer
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}

View File

@@ -311,8 +311,10 @@ const val emojiListField = "emoji_list"
const val completedByUserField = "completed_by_user"
const val completionDateField = "completion_date"
const val titleEntitiesField = "title_entities"
const val tasksField = "tasks"
const val othersCanAddTasksField = "others_can_add_tasks"
const val othersCanMarkTasksAsDoneField = "others_can_mark_tasks_as_done"
const val checklistField = "checklist"
const val requestContactField = "request_contact"
const val requestLocationField = "request_location"

View File

@@ -1,62 +1,181 @@
package dev.inmo.tgbotapi.types.checklists
import dev.inmo.micro_utils.common.Warning
import dev.inmo.tgbotapi.abstracts.TitledInput
import dev.inmo.tgbotapi.types.checklists.ChecklistTask.Input
import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.types.message.RawMessageEntity
import dev.inmo.tgbotapi.types.message.asTextSources
import dev.inmo.tgbotapi.types.message.parseModeField
import dev.inmo.tgbotapi.types.message.textsources.TextSource
import dev.inmo.tgbotapi.types.message.toRawMessageEntities
import dev.inmo.tgbotapi.types.othersCanAddTasksField
import dev.inmo.tgbotapi.types.othersCanMarkTasksAsDoneField
import dev.inmo.tgbotapi.types.tasksField
import dev.inmo.tgbotapi.types.titleEntitiesField
import dev.inmo.tgbotapi.types.titleField
import dev.inmo.tgbotapi.utils.EntitiesBuilder
import dev.inmo.tgbotapi.utils.EntitiesBuilderBody
import dev.inmo.tgbotapi.utils.extensions.makeSourceString
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable(Checklist.Companion::class)
data class Checklist(
val titleTextSources: List<TextSource>,
val tasks: List<ChecklistTask>,
val othersCanAddTasks: Boolean = false,
val othersCanCompleteTasks: Boolean = false,
) {
val title: String by lazy {
titleTextSources.makeSourceString()
@Serializable
sealed interface Checklist : TitledInput {
val tasks: List<ChecklistTask>
val othersCanAddTasks: Boolean
val othersCanCompleteTasks: Boolean
@Serializable
data class Input @Warning("It is low level API. Do not use it without need") constructor(
@SerialName(titleField)
override val title: String,
@SerialName(tasksField)
override val tasks: List<ChecklistTask.Input>,
@SerialName(parseModeField)
val parseMode: ParseMode? = null,
@SerialName(titleEntitiesField)
override val titleTextSources: List<TextSource> = emptyList(),
@SerialName(othersCanAddTasksField)
override val othersCanAddTasks: Boolean = false,
@SerialName(othersCanMarkTasksAsDoneField)
override val othersCanCompleteTasks: Boolean = false,
) : Checklist {
constructor(
text: String,
tasks: List<ChecklistTask.Input>,
parseMode: ParseMode? = null,
othersCanAddTasks: Boolean = false,
othersCanCompleteTasks: Boolean = false,
) : this(
title = text,
parseMode = parseMode,
titleTextSources = emptyList(),
tasks = tasks,
othersCanAddTasks = othersCanAddTasks,
othersCanCompleteTasks = othersCanCompleteTasks
)
constructor(
titleTextSources: List<TextSource>,
tasks: List<ChecklistTask.Input>,
othersCanAddTasks: Boolean = false,
othersCanCompleteTasks: Boolean = false,
) : this(
title = titleTextSources.makeSourceString(),
parseMode = null,
titleTextSources = titleTextSources,
tasks = tasks,
othersCanAddTasks = othersCanAddTasks,
othersCanCompleteTasks = othersCanCompleteTasks
)
constructor(
tasks: List<ChecklistTask.Input>,
othersCanAddTasks: Boolean = false,
othersCanCompleteTasks: Boolean = false,
builderBody: EntitiesBuilderBody
) : this(
titleTextSources = EntitiesBuilder().apply(builderBody).build(),
tasks = tasks,
othersCanAddTasks = othersCanAddTasks,
othersCanCompleteTasks = othersCanCompleteTasks
)
companion object : KSerializer<Input> {
@Serializable
private class RawChecklist(
val title: String,
val parseMode: ParseMode? = null,
val title_entities: List<RawMessageEntity> = emptyList(),
val tasks: List<ChecklistTask.Input>,
val others_can_add_tasks: Boolean = false,
val others_can_mark_tasks_as_done: Boolean = false,
)
override val descriptor: SerialDescriptor = RawChecklist.serializer().descriptor
override fun serialize(
encoder: Encoder,
value: Input
) {
RawChecklist.serializer().serialize(
encoder,
RawChecklist(
title = value.title,
title_entities = value.titleTextSources.toRawMessageEntities(),
tasks = value.tasks,
parseMode = value.parseMode,
others_can_add_tasks = value.othersCanAddTasks,
others_can_mark_tasks_as_done = value.othersCanCompleteTasks,
)
)
}
override fun deserialize(decoder: Decoder): Input {
val raw = RawChecklist.serializer().deserialize(decoder)
return Input(
title = raw.title,
titleTextSources = raw.title_entities.asTextSources(raw.title),
tasks = raw.tasks,
parseMode = raw.parseMode,
othersCanAddTasks = raw.others_can_add_tasks,
othersCanCompleteTasks = raw.others_can_mark_tasks_as_done
)
}
}
}
companion object : KSerializer<Checklist> {
@Serializable
private class RawChecklist(
val title: String,
val title_entities: List<RawMessageEntity> = emptyList(),
val tasks: List<ChecklistTask>,
val others_can_add_tasks: Boolean = false,
val others_can_mark_tasks_as_done: Boolean = false,
)
override val descriptor: SerialDescriptor = RawChecklist.serializer().descriptor
override fun serialize(
encoder: Encoder,
value: Checklist
) {
RawChecklist.serializer().serialize(
encoder,
RawChecklist(
title = value.title,
title_entities = value.titleTextSources.toRawMessageEntities(),
tasks = value.tasks,
others_can_add_tasks = value.othersCanAddTasks,
others_can_mark_tasks_as_done = value.othersCanCompleteTasks,
)
)
@Serializable(Created.Companion::class)
data class Created(
override val titleTextSources: List<TextSource>,
@SerialName(tasksField)
override val tasks: List<ChecklistTask.Created>,
@SerialName(othersCanAddTasksField)
override val othersCanAddTasks: Boolean = false,
@SerialName(othersCanMarkTasksAsDoneField)
override val othersCanCompleteTasks: Boolean = false,
): Checklist {
override val title: String by lazy {
titleTextSources.makeSourceString()
}
override fun deserialize(decoder: Decoder): Checklist {
val raw = RawChecklist.serializer().deserialize(decoder)
return Checklist(
titleTextSources = raw.title_entities.asTextSources(raw.title),
tasks = raw.tasks,
othersCanAddTasks = raw.others_can_add_tasks,
othersCanCompleteTasks = raw.others_can_mark_tasks_as_done
companion object : KSerializer<Created> {
@Serializable
private class RawChecklist(
val title: String,
val title_entities: List<RawMessageEntity> = emptyList(),
val tasks: List<ChecklistTask.Created>,
val others_can_add_tasks: Boolean = false,
val others_can_mark_tasks_as_done: Boolean = false,
)
override val descriptor: SerialDescriptor = RawChecklist.serializer().descriptor
override fun serialize(
encoder: Encoder,
value: Created
) {
RawChecklist.serializer().serialize(
encoder,
RawChecklist(
title = value.title,
title_entities = value.titleTextSources.toRawMessageEntities(),
tasks = value.tasks,
others_can_add_tasks = value.othersCanAddTasks,
others_can_mark_tasks_as_done = value.othersCanCompleteTasks,
)
)
}
override fun deserialize(decoder: Decoder): Created {
val raw = RawChecklist.serializer().deserialize(decoder)
return Created(
titleTextSources = raw.title_entities.asTextSources(raw.title),
tasks = raw.tasks,
othersCanAddTasks = raw.others_can_add_tasks,
othersCanCompleteTasks = raw.others_can_mark_tasks_as_done
)
}
}
}
}

View File

@@ -1,19 +1,28 @@
package dev.inmo.tgbotapi.types.checklists
import dev.inmo.micro_utils.common.Warning
import dev.inmo.tgbotapi.abstracts.TextedInput
import dev.inmo.tgbotapi.abstracts.TitledInput
import dev.inmo.tgbotapi.types.TelegramDate
import dev.inmo.tgbotapi.types.chat.PreviewUser
import dev.inmo.tgbotapi.types.completedByUserField
import dev.inmo.tgbotapi.types.completionDateField
import dev.inmo.tgbotapi.types.idField
import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.types.message.RawMessageEntity
import dev.inmo.tgbotapi.types.message.asTextSources
import dev.inmo.tgbotapi.types.message.parseModeField
import dev.inmo.tgbotapi.types.message.textsources.RegularTextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.message.toRawMessageEntities
import dev.inmo.tgbotapi.types.tasksField
import dev.inmo.tgbotapi.types.textEntitiesField
import dev.inmo.tgbotapi.types.textField
import dev.inmo.tgbotapi.types.textParseModeField
import dev.inmo.tgbotapi.types.titleEntitiesField
import dev.inmo.tgbotapi.types.titleField
import dev.inmo.tgbotapi.utils.EntitiesBuilder
import dev.inmo.tgbotapi.utils.EntitiesBuilderBody
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.extensions.makeSourceString
import kotlinx.serialization.EncodeDefault
@@ -29,18 +38,81 @@ import kotlinx.serialization.encoding.Encoder
sealed interface ChecklistTask : TextedInput {
val id: ChecklistTaskId
override val text: String
val completedByUser: PreviewUser?
get() = null
val completionDate: TelegramDate?
get() = null
@Serializable
@Serializable(Input.Companion::class)
data class Input @Warning("It is low level API. Do not use it without need") constructor(
@SerialName(idField)
override val id: ChecklistTaskId,
@SerialName(textField)
override val text: String,
@SerialName(textParseModeField)
val parseMode: ParseMode? = null,
@SerialName(textEntitiesField)
override val textSources: List<TextSource> = emptyList(),
) : ChecklistTask {
constructor(id: ChecklistTaskId, text: String, parseMode: ParseMode? = null) : this(
id = id,
text = text,
parseMode = parseMode,
textSources = emptyList()
)
constructor(id: ChecklistTaskId, textSources: List<TextSource>) : this(
id = id,
text = textSources.makeSourceString(),
parseMode = null,
textSources = textSources
)
constructor(id: ChecklistTaskId, builderBody: EntitiesBuilderBody) : this(
id = id,
textSources = EntitiesBuilder().apply(builderBody).build()
)
companion object : KSerializer<Input> {
@Serializable
private data class RawInput(
@SerialName(idField)
val id: ChecklistTaskId,
@SerialName(textField)
val text: String,
@SerialName(textParseModeField)
val parseMode: ParseMode? = null,
@SerialName(textEntitiesField)
val textSources: List<RawMessageEntity> = emptyList(),
)
override val descriptor: SerialDescriptor
get() = RawInput.serializer().descriptor
override fun deserialize(decoder: Decoder): Input {
val raw = RawInput.serializer().deserialize(decoder)
return Input(
raw.id,
raw.text,
raw.parseMode,
raw.textSources.asTextSources(raw.text)
)
}
override fun serialize(encoder: Encoder, value: Input) {
RawInput.serializer().serialize(
encoder,
RawInput(
value.id,
value.text,
value.parseMode,
value.textSources.toRawMessageEntities()
)
)
}
}
}
@Serializable(Created.Serializer::class)
data class Undone(
@SerialName(idField)
override val id: ChecklistTaskId,
@SerialName(textEntitiesField)
override val textSources: List<TextSource> = emptyList(),
) : ChecklistTask {
) : ChecklistTask.Created {
@OptIn(ExperimentalSerializationApi::class)
@EncodeDefault
@Serializable
@@ -55,7 +127,7 @@ sealed interface ChecklistTask : TextedInput {
)
}
@Serializable
@Serializable(Created.Serializer::class)
data class Done(
@SerialName(idField)
override val id: ChecklistTaskId,
@@ -65,7 +137,7 @@ sealed interface ChecklistTask : TextedInput {
override val completionDate: TelegramDate,
@SerialName(textEntitiesField)
override val textSources: List<TextSource> = emptyList()
) : ChecklistTask {
) : ChecklistTask.Created {
@OptIn(ExperimentalSerializationApi::class)
@EncodeDefault
@Serializable
@@ -86,55 +158,62 @@ sealed interface ChecklistTask : TextedInput {
)
)
}
@RiskFeature
object Serializer : KSerializer<ChecklistTask> {
@Serializable
private data class RawChecklistTask(
@SerialName(idField)
val id: ChecklistTaskId,
@SerialName(textField)
val text: String,
@SerialName(textEntitiesField)
val textSources: List<RawMessageEntity> = emptyList(),
@SerialName(completedByUserField)
val completedByUser: PreviewUser? = null,
@SerialName(completionDateField)
val completionDate: TelegramDate = TelegramDate(0), // TelegramDate(0) is the default according to https://core.telegram.org/bots/api#checklisttask
)
override val descriptor: SerialDescriptor = RawChecklistTask.serializer().descriptor
override fun deserialize(decoder: Decoder): ChecklistTask {
val raw = RawChecklistTask.serializer().deserialize(
decoder
@Serializable(Created.Serializer::class)
sealed interface Created : ChecklistTask {
val completedByUser: PreviewUser?
get() = null
val completionDate: TelegramDate?
get() = null
@RiskFeature
object Serializer : KSerializer<Created> {
@Serializable
private data class RawCreatedChecklistTask(
@SerialName(idField)
val id: ChecklistTaskId,
@SerialName(textField)
val text: String,
@SerialName(textEntitiesField)
val textSources: List<RawMessageEntity> = emptyList(),
@SerialName(completedByUserField)
val completedByUser: PreviewUser? = null,
@SerialName(completionDateField)
val completionDate: TelegramDate = TelegramDate(0), // TelegramDate(0) is the default according to https://core.telegram.org/bots/api#checklisttask
)
override val descriptor: SerialDescriptor = RawCreatedChecklistTask.serializer().descriptor
return when {
raw.completedByUser != null -> Done(
id = raw.id,
completedByUser = raw.completedByUser,
completionDate = raw.completionDate,
textSources = raw.textSources.asTextSources(raw.text),
override fun deserialize(decoder: Decoder): Created {
val raw = RawCreatedChecklistTask.serializer().deserialize(
decoder
)
else -> Undone(
id = raw.id,
textSources = raw.textSources.asTextSources(raw.text),
return when {
raw.completedByUser != null -> Done(
id = raw.id,
completedByUser = raw.completedByUser,
completionDate = raw.completionDate,
textSources = raw.textSources.asTextSources(raw.text),
)
else -> Undone(
id = raw.id,
textSources = raw.textSources.asTextSources(raw.text),
)
}
}
override fun serialize(encoder: Encoder, value: Created) {
RawCreatedChecklistTask.serializer().serialize(
encoder,
RawCreatedChecklistTask(
id = value.id,
text = value.text,
completedByUser = value.completedByUser,
completionDate = value.completionDate ?: TelegramDate(0),
textSources = value.textSources.toRawMessageEntities()
)
)
}
}
override fun serialize(encoder: Encoder, value: ChecklistTask) {
RawChecklistTask.serializer().serialize(
encoder,
RawChecklistTask(
id = value.id,
text = value.text,
completedByUser = value.completedByUser,
completionDate = value.completionDate ?: TelegramDate(0),
textSources = value.textSources.toRawMessageEntities()
)
)
}
}
}

View File

@@ -6,5 +6,5 @@ import kotlin.jvm.JvmInline
@Serializable
@JvmInline
value class ChecklistTaskId(
val int: Int
val int: UInt
)

View File

@@ -0,0 +1,15 @@
package dev.inmo.tgbotapi.types.checklists
import dev.inmo.micro_utils.common.Warning
import dev.inmo.tgbotapi.abstracts.TextedInput
import dev.inmo.tgbotapi.abstracts.TitledInput
import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.types.message.parseModeField
import dev.inmo.tgbotapi.types.message.textsources.TextSource
import dev.inmo.tgbotapi.types.tasksField
import dev.inmo.tgbotapi.types.titleEntitiesField
import dev.inmo.tgbotapi.types.titleField
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -165,6 +165,10 @@ internal data class RawMessage(
private val giveaway_winners: GiveawayPublicResults? = null,
private val giveaway_completed: GiveawayPrivateResults? = null,
// Checklists
private val checklist_tasks_done: Nothing,
private val checklist_tasks_added: Nothing,
// Gifts
private val gift: GiftSentOrReceived.Regular? = null,
private val unique_gift: GiftSentOrReceived.Unique? = null,

View File

@@ -0,0 +1,32 @@
package dev.inmo.tgbotapi.types.message.content
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.EffectId
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.checklists.Checklist
import dev.inmo.tgbotapi.types.message.abstracts.AccessibleMessage
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import kotlinx.serialization.Serializable
@Serializable
data class ChecklistContent(
val checklist: Checklist
) : MessageContent {
override fun createResend(
chatId: ChatIdentifier,
messageThreadId: MessageThreadId?,
businessConnectionId: BusinessConnectionId?,
disableNotification: Boolean,
protectContent: Boolean,
allowPaidBroadcast: Boolean,
effectId: EffectId?,
replyParameters: ReplyParameters?,
replyMarkup: KeyboardMarkup?
): Request<CommonMessage<ChecklistContent>> {
return SendChecklist
}
}