diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/abstracts/Texted.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/abstracts/Texted.kt index e2351b5994..a52c86b5c9 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/abstracts/Texted.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/abstracts/Texted.kt @@ -27,3 +27,12 @@ interface TextedOutput : ParsableOutput, EntitiesOutput interface TextedInput : TextedWithTextSources { override val textSources: List } + +interface TitledInput : TextedInput { + val title: String + val titleTextSources: List + override val text: String + get() = title + override val textSources: List + get() = titleTextSources +} 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 53aabe0333..8bf51d5140 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 @@ -311,6 +311,7 @@ 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" diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/Checklist.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/Checklist.kt index dd531ff73a..e8e9751ddd 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/Checklist.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/Checklist.kt @@ -1,62 +1,132 @@ package dev.inmo.tgbotapi.types.checklists +import dev.inmo.micro_utils.common.Warning +import dev.inmo.tgbotapi.abstracts.TitledInput +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.tasksField +import dev.inmo.tgbotapi.types.titleEntitiesField +import dev.inmo.tgbotapi.types.titleField 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, - val tasks: List, - val othersCanAddTasks: Boolean = false, - val othersCanCompleteTasks: Boolean = false, -) { - val title: String by lazy { - titleTextSources.makeSourceString() +@Serializable +sealed interface Checklist : TitledInput { + val tasks: List + 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, + @SerialName(parseModeField) + val parseMode: ParseMode? = null, + @SerialName(titleEntitiesField) + override val titleTextSources: List = emptyList(), + override val othersCanAddTasks: Boolean = false, + override val othersCanCompleteTasks: Boolean = false, + ) : Checklist { + companion object : KSerializer { + @Serializable + private class RawChecklist( + val title: String, + val parseMode: ParseMode? = null, + val title_entities: List = emptyList(), + val tasks: List, + 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 { - @Serializable - private class RawChecklist( - val title: String, - val title_entities: List = emptyList(), - val tasks: List, - 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, + override val tasks: List, + override val othersCanAddTasks: Boolean = false, + 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 { + @Serializable + private class RawChecklist( + val title: String, + val title_entities: List = emptyList(), + val tasks: List, + 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 + ) + } } } } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/ChecklistTask.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/ChecklistTask.kt index 3a9af155e9..71702f07a7 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/ChecklistTask.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/ChecklistTask.kt @@ -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,93 @@ 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 + data class InputChecklist @Warning("It is low level API. Do not use it without need") constructor( + @SerialName(titleField) + override val title: String, + @SerialName(tasksField) + val tasks: List, + @SerialName(parseModeField) + val parseMode: ParseMode? = null, + @SerialName(titleEntitiesField) + override val titleTextSources: List = emptyList(), + + ) : TitledInput + + @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 = emptyList(), + ) : ChecklistTask { + constructor(id: ChecklistTaskId, text: String, parseMode: ParseMode? = null) : this( + id = id, + text = text, + parseMode = parseMode, + textSources = emptyList() + ) + constructor(id: ChecklistTaskId, textSources: List) : 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 { + @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 = 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 = emptyList(), - ) : ChecklistTask { + ) : ChecklistTask.Created { @OptIn(ExperimentalSerializationApi::class) @EncodeDefault @Serializable @@ -55,7 +139,7 @@ sealed interface ChecklistTask : TextedInput { ) } - @Serializable + @Serializable(Created.Serializer::class) data class Done( @SerialName(idField) override val id: ChecklistTaskId, @@ -65,7 +149,7 @@ sealed interface ChecklistTask : TextedInput { override val completionDate: TelegramDate, @SerialName(textEntitiesField) override val textSources: List = emptyList() - ) : ChecklistTask { + ) : ChecklistTask.Created { @OptIn(ExperimentalSerializationApi::class) @EncodeDefault @Serializable @@ -86,55 +170,62 @@ sealed interface ChecklistTask : TextedInput { ) ) } - - - @RiskFeature - object Serializer : KSerializer { - @Serializable - private data class RawChecklistTask( - @SerialName(idField) - val id: ChecklistTaskId, - @SerialName(textField) - val text: String, - @SerialName(textEntitiesField) - val textSources: List = 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 { + @Serializable + private data class RawCreatedChecklistTask( + @SerialName(idField) + val id: ChecklistTaskId, + @SerialName(textField) + val text: String, + @SerialName(textEntitiesField) + val textSources: List = 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() - ) - ) - } } } \ No newline at end of file diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/ChecklistTaskId.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/ChecklistTaskId.kt index 3d50703dc4..c5df6abd6a 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/ChecklistTaskId.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/ChecklistTaskId.kt @@ -6,5 +6,5 @@ import kotlin.jvm.JvmInline @Serializable @JvmInline value class ChecklistTaskId( - val int: Int + val int: UInt ) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/InputChecklist.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/InputChecklist.kt new file mode 100644 index 0000000000..1baf755a5f --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/checklists/InputChecklist.kt @@ -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 + +