1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2026-07-02 16:15:24 +00:00

Merge pull request #1052 from InsanusMokrassar/35.0.0

35.0.0
This commit is contained in:
2026-07-01 15:43:02 +06:00
committed by GitHub
53 changed files with 9009 additions and 2619 deletions

View File

@@ -1,5 +1,43 @@
# TelegramBotAPI changelog
## 35.0.0
**THIS UPDATE CONTAINS SUPPORT OF [TELEGRAM BOTS API 10.1](https://core.telegram.org/bots/api-changelog#june-11-2026)**
**Breaking changes**:
* `EditTextChatMessage.text` changed from `String` to `String?` (rich edits carry no text) — implementations overriding it must widen the type and readers must handle `null`
* `EditChatMessageText.text` changed from `String` to `String?`; its `textSources` is now `null` when there is no text — code reading `text` as non-null must handle `null`
* `ExtendedBot` gained the `supportsJoinRequestQueries` field inserted between `supportsGuestQueries` and `canConnectToBusiness` — positional constructor calls and `componentN` destructuring shift and must be updated
**Migration advice**: Handle nullable `text` on `EditTextChatMessage`/`EditChatMessageText`; construct `ExtendedBot` with named arguments
* `Core`:
* (`Rich Messages`) Added `RichText` (`RichTextPlain`, `RichTextGroup`, `RichTextEntity`) hierarchy with all 25 `RichText*` entity types and a recursive serializer supporting plain strings, arrays and typed objects; every `RichText` exposes `rawText`, `markdown` and `html` renderings
* (`Rich Messages`) Added `RichBlock` hierarchy with all 21 `RichBlock*` types plus `RichBlockCaption`, `RichBlockTableCell` and `RichBlockListItem`; every `RichBlock` exposes `markdown`/`html` source, the `RichBlockMedia` marker interface and `subBlocks`/`search` navigation helpers
* (`Rich Messages`) Added `RichTextInfo` type (with `markdown`/`html` source built from its blocks) and `RichMessageContent` message content; `RichMessageContent.createResend` re-sends the content (forwarded when it contains media blocks, otherwise re-built through `SendRichMessage`); parsed `rich_message` in `RawMessage`; added `RichMessageContentMessage` typealias
* (`Rich Messages`) Added `InputRichMessage` (internal constructor with `InputRichMessageHTML`/`InputRichMessageMarkdown` factories) and `InputRichMessageContent` usable as `InputMessageContent`
* (`Rich Messages`) Added Rich Messages DSL builders `buildRichText`/`RichTextBuilder`, `buildRichBlocks`/`RichBlocksBuilder`, `buildRichTextInfo` and `RichBlockListBuilder` (marked with the `@RichTextDsl` DSL marker)
* (`Rich Messages`) Added Rich Markdown/HTML source helpers `String.escapeRichMarkdown()` and `List<RichBlock>.toRichMarkdown()`/`toRichHtml()`
* (`Rich Messages`) Added `SendRichMessage` and `SendRichMessageDraft` requests
* (`Rich Messages`) Added `richMessage` parameter to `EditChatMessageText`; its `text` is now nullable (per API) and the `EditChatMessageRichText` factory builds a rich edit; widened `EditTextChatMessage.text` to nullable
* (`Polls`) Added `Link` type (`dev.inmo.tgbotapi.types.Link`) implementing `PollMedia`, carrying the `url` of a link attached to a poll option
* (`Polls`) Added `link` field parsing to `PollMedia` deserialization/serialization
* (`Polls`) Added `TelegramMediaLink` (`InputMediaLink`) implementing `InputPollOptionMedia`, usable as a poll option media
* (`Join Request Queries`) Added `ChatJoinRequestQueryId` value class and `queryId` field to `ChatJoinRequest`
* (`Join Request Queries`) Added `supportsJoinRequestQueries` flag to `ExtendedBot` (`User.supports_join_request_queries`)
* (`Join Request Queries`) Added `guardBot` field to `ExtendedChat` (`ChatFullInfo.guard_bot`), parsed for public chats
* (`Join Request Queries`) Added `AnswerChatJoinRequestQuery` request and `ChatJoinRequestQueryResult` sealed interface (`Approve`/`Decline`/`Queue`/`Unknown`) serialized as a plain string
* (`Join Request Queries`) Added `SendChatJoinRequestWebApp` request
* `API`:
* (`Rich Messages`) Added `sendRichMessage` and `sendRichMessageDraft` `TelegramBot` extensions
* (`Join Request Queries`) Added `answerChatJoinRequestQuery` and `sendChatJoinRequestWebApp` `TelegramBot` extensions
* `BehaviourBuilder`:
* (`Rich Messages`) Added `onRichMessage` trigger and `waitRichMessage`/`waitRichMessageMessage` expectations
* `Utils`:
* (`Rich Messages`) Added `Flow<ContentMessage<*>>.onlyRichMessageContentMessages()`
* (`Join Request Queries`) Added `ChatJoinRequest.query_id` raw accessor
## 34.0.0
**THIS UPDATE CONTAINS SUPPORT OF [TELEGRAM BOTS API 10.0](https://core.telegram.org/bots/api-changelog#may-8-2026)**

View File

@@ -1,4 +1,4 @@
# TelegramBotAPI [![Maven Central Version](https://img.shields.io/maven-central/v/dev.inmo/tgbotapi)](https://central.sonatype.com/artifact/dev.inmo/tgbotapi) [![Supported version](https://img.shields.io/badge/Telegram%20Bot%20API-10.0-blue)](https://core.telegram.org/bots/api-changelog#may-8-2026)
# TelegramBotAPI [![Maven Central Version](https://img.shields.io/maven-central/v/dev.inmo/tgbotapi)](https://central.sonatype.com/artifact/dev.inmo/tgbotapi) [![Supported version](https://img.shields.io/badge/Telegram%20Bot%20API-10.1-blue)](https://core.telegram.org/bots/api-changelog#june-11-2026)
| Docs | [![KDocs](https://img.shields.io/static/v1?label=Dokka&message=KDocs&color=blue&logo=kotlin)](https://tgbotapi.inmo.dev/index.html) [![Mini tutorial](https://img.shields.io/static/v1?label=Mk&message=Docs&color=blue&logo=mkdocs)](https://docs.inmo.dev/tgbotapi/index.html) |
|:----------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|

View File

@@ -48,3 +48,15 @@ Each section can be ommited if there are no any changes in the section. In case
* `Core`:
* (`Guest mode`) // change data
```
---
When a property (or method) can be declared on an interface/class and implemented by each inheritor - it MUST be declared there and overridden per implementation. DO NOT implement such behaviour as a common extension property/function that dispatches with a `when (this)` over every subtype: those branches silently drift out of sync when new subtypes are added and are not part of the type contract. If a part of the implementation is reused across several inheritors, keep that part in a shared helper (a companion-object function of the owner, or an `internal` top-level function for logic shared between different types) and let the overrides reuse it.
---
All work MUST be performed in the normal (main) working worktree of the repository. DO NOT create or do the work inside a separate worktree (for example `.claude/worktrees/...`): commits made there leave the main checkout behind and force an extra fast-forward/pull to land the changes. If some environment forces an isolated worktree, fast-forward the main branch onto those commits and continue in the main worktree.
---
When a prompt contains a list of several subtasks, run each subtask as its own subagent (one subagent per subtask) instead of doing all of them inline in the main thread.

View File

@@ -6,4 +6,4 @@ kotlin.incremental=true
kotlin.incremental.js=true
library_group=dev.inmo
library_version=34.0.0
library_version=35.0.0

View File

@@ -562,6 +562,12 @@ public final class dev/inmo/tgbotapi/extensions/api/chat/get/GetForumTopicIconSt
public static final fun getForumTopicIconStickers (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class dev/inmo/tgbotapi/extensions/api/chat/invite_links/AnswerChatJoinRequestQueryKt {
public static final fun answerChatJoinRequestQuery (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/chat/ChatJoinRequest;Ldev/inmo/tgbotapi/requests/chat/invite_links/ChatJoinRequestQueryResult;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun answerChatJoinRequestQuery (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/update/ChatJoinRequestUpdate;Ldev/inmo/tgbotapi/requests/chat/invite_links/ChatJoinRequestQueryResult;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun answerChatJoinRequestQuery-Vwi-J50 (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ljava/lang/String;Ldev/inmo/tgbotapi/requests/chat/invite_links/ChatJoinRequestQueryResult;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class dev/inmo/tgbotapi/extensions/api/chat/invite_links/ApproveChatJoinRequestKt {
public static final fun approve (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/chat/ChatJoinRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun approveChatJoinRequest (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/ChatIdentifier;Ldev/inmo/tgbotapi/types/IdChatIdentifier;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -687,6 +693,12 @@ public final class dev/inmo/tgbotapi/extensions/api/chat/invite_links/RevokeChat
public static final fun revokeChatInviteLink (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/chat/PublicChat;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class dev/inmo/tgbotapi/extensions/api/chat/invite_links/SendChatJoinRequestWebAppKt {
public static final fun sendChatJoinRequestWebApp (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/chat/ChatJoinRequest;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun sendChatJoinRequestWebApp (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/update/ChatJoinRequestUpdate;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun sendChatJoinRequestWebApp-Vwi-J50 (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class dev/inmo/tgbotapi/extensions/api/chat/members/BanChatMemberKt {
public static final fun banChatMember (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/ChatIdentifier;Ldev/inmo/tgbotapi/types/IdChatIdentifier;Ldev/inmo/tgbotapi/types/TelegramDate;Ljava/lang/Boolean;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun banChatMember (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/IdChatIdentifier;Ldev/inmo/tgbotapi/types/chat/User;Ldev/inmo/tgbotapi/types/TelegramDate;Ljava/lang/Boolean;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -1950,6 +1962,18 @@ public final class dev/inmo/tgbotapi/extensions/api/send/SendMessageKt {
public static synthetic fun sendTextMessage-kPvWKIg$default (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/chat/Chat;Ljava/lang/String;Ldev/inmo/tgbotapi/types/message/ParseMode;Ldev/inmo/tgbotapi/types/LinkPreviewOptions;Ldev/inmo/tgbotapi/types/MessageThreadId;Ldev/inmo/tgbotapi/types/DirectMessageThreadId;Ljava/lang/String;ZZZLjava/lang/String;Ldev/inmo/tgbotapi/types/message/SuggestedPostParameters;Ldev/inmo/tgbotapi/types/ReplyParameters;Ldev/inmo/tgbotapi/types/buttons/KeyboardMarkup;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}
public final class dev/inmo/tgbotapi/extensions/api/send/SendRichMessageDraftKt {
public static final fun sendRichMessageDraft-tvXF6p8 (Ldev/inmo/tgbotapi/bot/RequestsExecutor;JJLdev/inmo/tgbotapi/types/rich/InputRichMessage;Ldev/inmo/tgbotapi/types/MessageThreadId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun sendRichMessageDraft-tvXF6p8$default (Ldev/inmo/tgbotapi/bot/RequestsExecutor;JJLdev/inmo/tgbotapi/types/rich/InputRichMessage;Ldev/inmo/tgbotapi/types/MessageThreadId;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}
public final class dev/inmo/tgbotapi/extensions/api/send/SendRichMessageKt {
public static final fun sendRichMessage-mNzvAxs (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/ChatIdentifier;Ldev/inmo/tgbotapi/types/rich/InputRichMessage;Ldev/inmo/tgbotapi/types/MessageThreadId;Ldev/inmo/tgbotapi/types/DirectMessageThreadId;Ljava/lang/String;ZZZLjava/lang/String;Ldev/inmo/tgbotapi/types/message/SuggestedPostParameters;Ldev/inmo/tgbotapi/types/ReplyParameters;Ldev/inmo/tgbotapi/types/buttons/KeyboardMarkup;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun sendRichMessage-mNzvAxs (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/chat/Chat;Ldev/inmo/tgbotapi/types/rich/InputRichMessage;Ldev/inmo/tgbotapi/types/MessageThreadId;Ldev/inmo/tgbotapi/types/DirectMessageThreadId;Ljava/lang/String;ZZZLjava/lang/String;Ldev/inmo/tgbotapi/types/message/SuggestedPostParameters;Ldev/inmo/tgbotapi/types/ReplyParameters;Ldev/inmo/tgbotapi/types/buttons/KeyboardMarkup;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun sendRichMessage-mNzvAxs$default (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/ChatIdentifier;Ldev/inmo/tgbotapi/types/rich/InputRichMessage;Ldev/inmo/tgbotapi/types/MessageThreadId;Ldev/inmo/tgbotapi/types/DirectMessageThreadId;Ljava/lang/String;ZZZLjava/lang/String;Ldev/inmo/tgbotapi/types/message/SuggestedPostParameters;Ldev/inmo/tgbotapi/types/ReplyParameters;Ldev/inmo/tgbotapi/types/buttons/KeyboardMarkup;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static synthetic fun sendRichMessage-mNzvAxs$default (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/chat/Chat;Ldev/inmo/tgbotapi/types/rich/InputRichMessage;Ldev/inmo/tgbotapi/types/MessageThreadId;Ldev/inmo/tgbotapi/types/DirectMessageThreadId;Ljava/lang/String;ZZZLjava/lang/String;Ldev/inmo/tgbotapi/types/message/SuggestedPostParameters;Ldev/inmo/tgbotapi/types/ReplyParameters;Ldev/inmo/tgbotapi/types/buttons/KeyboardMarkup;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}
public final class dev/inmo/tgbotapi/extensions/api/send/SendStaticLocationKt {
public static final fun sendLocation-Z2YO6e4 (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/ChatIdentifier;DDLdev/inmo/tgbotapi/types/MessageThreadId;Ldev/inmo/tgbotapi/types/DirectMessageThreadId;Ljava/lang/String;ZZZLjava/lang/String;Ldev/inmo/tgbotapi/types/message/SuggestedPostParameters;Ldev/inmo/tgbotapi/types/ReplyParameters;Ldev/inmo/tgbotapi/types/buttons/KeyboardMarkup;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun sendLocation-Z2YO6e4 (Ldev/inmo/tgbotapi/bot/RequestsExecutor;Ldev/inmo/tgbotapi/types/chat/Chat;DDLdev/inmo/tgbotapi/types/MessageThreadId;Ldev/inmo/tgbotapi/types/DirectMessageThreadId;Ljava/lang/String;ZZZLjava/lang/String;Ldev/inmo/tgbotapi/types/message/SuggestedPostParameters;Ldev/inmo/tgbotapi/types/ReplyParameters;Ldev/inmo/tgbotapi/types/buttons/KeyboardMarkup;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

View File

@@ -0,0 +1,28 @@
package dev.inmo.tgbotapi.extensions.api.chat.invite_links
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.chat.invite_links.AnswerChatJoinRequestQuery
import dev.inmo.tgbotapi.requests.chat.invite_links.ChatJoinRequestQueryResult
import dev.inmo.tgbotapi.types.ChatJoinRequestQueryId
import dev.inmo.tgbotapi.types.chat.ChatJoinRequest
import dev.inmo.tgbotapi.types.update.ChatJoinRequestUpdate
public suspend fun TelegramBot.answerChatJoinRequestQuery(
chatJoinRequestQueryId: ChatJoinRequestQueryId,
result: ChatJoinRequestQueryResult
): Unit = execute(AnswerChatJoinRequestQuery(chatJoinRequestQueryId, result))
public suspend fun TelegramBot.answerChatJoinRequestQuery(
chatJoinRequest: ChatJoinRequest,
result: ChatJoinRequestQueryResult
): Unit = answerChatJoinRequestQuery(
requireNotNull(chatJoinRequest.queryId) {
"ChatJoinRequest.queryId is null, this request can't be answered with answerChatJoinRequestQuery"
},
result
)
public suspend fun TelegramBot.answerChatJoinRequestQuery(
chatJoinRequestUpdate: ChatJoinRequestUpdate,
result: ChatJoinRequestQueryResult
): Unit = answerChatJoinRequestQuery(chatJoinRequestUpdate.data, result)

View File

@@ -0,0 +1,27 @@
package dev.inmo.tgbotapi.extensions.api.chat.invite_links
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.chat.invite_links.SendChatJoinRequestWebApp
import dev.inmo.tgbotapi.types.ChatJoinRequestQueryId
import dev.inmo.tgbotapi.types.chat.ChatJoinRequest
import dev.inmo.tgbotapi.types.update.ChatJoinRequestUpdate
public suspend fun TelegramBot.sendChatJoinRequestWebApp(
chatJoinRequestQueryId: ChatJoinRequestQueryId,
webAppUrl: String
): Unit = execute(SendChatJoinRequestWebApp(chatJoinRequestQueryId, webAppUrl))
public suspend fun TelegramBot.sendChatJoinRequestWebApp(
chatJoinRequest: ChatJoinRequest,
webAppUrl: String
): Unit = sendChatJoinRequestWebApp(
requireNotNull(chatJoinRequest.queryId) {
"ChatJoinRequest.queryId is null, this request can't be processed with sendChatJoinRequestWebApp"
},
webAppUrl
)
public suspend fun TelegramBot.sendChatJoinRequestWebApp(
chatJoinRequestUpdate: ChatJoinRequestUpdate,
webAppUrl: String
): Unit = sendChatJoinRequestWebApp(chatJoinRequestUpdate.data, webAppUrl)

View File

@@ -0,0 +1,70 @@
package dev.inmo.tgbotapi.extensions.api.send
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.send.SendRichMessage
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.chat.Chat
import dev.inmo.tgbotapi.types.message.SuggestedPostParameters
import dev.inmo.tgbotapi.types.message.abstracts.ChatContentMessage
import dev.inmo.tgbotapi.types.message.content.RichMessageContent
import dev.inmo.tgbotapi.types.rich.InputRichMessage
public suspend fun TelegramBot.sendRichMessage(
chatId: ChatIdentifier,
richMessage: InputRichMessage,
threadId: MessageThreadId? = chatId.threadId,
directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
businessConnectionId: BusinessConnectionId? = chatId.businessConnectionId,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowPaidBroadcast: Boolean = false,
effectId: EffectId? = null,
suggestedPostParameters: SuggestedPostParameters? = null,
replyParameters: ReplyParameters? = null,
replyMarkup: KeyboardMarkup? = null
): ChatContentMessage<RichMessageContent> = execute(
SendRichMessage(
chatId = chatId,
richMessage = richMessage,
threadId = threadId,
directMessageThreadId = directMessageThreadId,
businessConnectionId = businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
allowPaidBroadcast = allowPaidBroadcast,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)
)
public suspend fun TelegramBot.sendRichMessage(
chat: Chat,
richMessage: InputRichMessage,
threadId: MessageThreadId? = chat.id.threadId,
directMessageThreadId: DirectMessageThreadId? = chat.id.directMessageThreadId,
businessConnectionId: BusinessConnectionId? = chat.id.businessConnectionId,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowPaidBroadcast: Boolean = false,
effectId: EffectId? = null,
suggestedPostParameters: SuggestedPostParameters? = null,
replyParameters: ReplyParameters? = null,
replyMarkup: KeyboardMarkup? = null
): ChatContentMessage<RichMessageContent> = sendRichMessage(
chatId = chat.id,
richMessage = richMessage,
threadId = threadId,
directMessageThreadId = directMessageThreadId,
businessConnectionId = businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
allowPaidBroadcast = allowPaidBroadcast,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)

View File

@@ -0,0 +1,21 @@
package dev.inmo.tgbotapi.extensions.api.send
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.send.SendRichMessageDraft
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.rich.InputRichMessage
public suspend fun TelegramBot.sendRichMessageDraft(
chatId: ChatId,
draftId: Long,
richMessage: InputRichMessage,
threadId: MessageThreadId? = chatId.threadId
): Unit = execute(
SendRichMessageDraft(
chatId = chatId,
draftId = draftId,
richMessage = richMessage,
threadId = threadId
)
)

View File

@@ -394,6 +394,8 @@ public final class dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/W
public static synthetic fun waitPhoto$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun waitPoll (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun waitPoll$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun waitRichMessage (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun waitRichMessage$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun waitStaticLocation (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun waitStaticLocation$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun waitSticker (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
@@ -467,6 +469,8 @@ public final class dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/W
public static synthetic fun waitPhotoMessage$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun waitPollMessage (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun waitPollMessage$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun waitRichMessageMessage (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun waitRichMessageMessage$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun waitStaticLocationMessage (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun waitStaticLocationMessage$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun waitStickerMessage (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/requests/abstracts/Request;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
@@ -1299,6 +1303,8 @@ public final class dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handl
public static synthetic fun onPhoto$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/SimpleFilter;Lkotlin/jvm/functions/Function4;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/marker_factories/MarkerFactory;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
public static final fun onPoll (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/SimpleFilter;Lkotlin/jvm/functions/Function4;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/marker_factories/MarkerFactory;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/Job;
public static synthetic fun onPoll$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/SimpleFilter;Lkotlin/jvm/functions/Function4;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/marker_factories/MarkerFactory;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
public static final fun onRichMessage (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/SimpleFilter;Lkotlin/jvm/functions/Function4;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/marker_factories/MarkerFactory;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/Job;
public static synthetic fun onRichMessage$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/SimpleFilter;Lkotlin/jvm/functions/Function4;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/marker_factories/MarkerFactory;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
public static final fun onStaticLocation (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/SimpleFilter;Lkotlin/jvm/functions/Function4;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/marker_factories/MarkerFactory;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/Job;
public static synthetic fun onStaticLocation$default (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/SimpleFilter;Lkotlin/jvm/functions/Function4;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/marker_factories/MarkerFactory;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
public static final fun onSticker (Ldev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/SimpleFilter;Lkotlin/jvm/functions/Function4;Ldev/inmo/tgbotapi/extensions/behaviour_builder/utils/marker_factories/MarkerFactory;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/Job;

View File

@@ -66,6 +66,10 @@ fun BehaviourContext.waitStory(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent(initRequest, errorFactory).mapContent<StoryContent>()
fun BehaviourContext.waitRichMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent(initRequest, errorFactory).mapContent<RichMessageContent>()
fun BehaviourContext.waitVenue(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }

View File

@@ -75,6 +75,10 @@ fun BehaviourContext.waitStoryMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage(initRequest, errorFactory).mapWithContent<StoryContent>()
fun BehaviourContext.waitRichMessageMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage(initRequest, errorFactory).mapWithContent<RichMessageContent>()
fun BehaviourContext.waitVenueMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }

View File

@@ -304,6 +304,33 @@ fun <BC : BehaviourContext> BC.onStory(
scenarioReceiver
)
/**
* @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call
* @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example,
* this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage].
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own.
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times]
* to combinate several filters
* @param [markerFactory] **Pass null to handle requests fully parallel**. Will be used to identify different "stream".
* [scenarioReceiver] will be called synchronously in one "stream". Output of [markerFactory] will be used as a key for
* "stream"
* @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that
* data
*/
fun <BC : BehaviourContext> BC.onRichMessage(
initialFilter: CommonMessageFilter<RichMessageContent>? = CommonMessageFilterExcludeMediaGroups,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, RichMessageContentMessage, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in RichMessageContentMessage, Any>? = ByChatMessageMarkerFactory,
additionalSubcontextInitialAction: CustomBehaviourContextAndTwoTypesReceiver<BC, Unit, Update, RichMessageContentMessage>? = null,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, RichMessageContentMessage>
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
additionalSubcontextInitialAction,
scenarioReceiver
)
/**
* @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call
* @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
package dev.inmo.tgbotapi.requests.chat.invite_links
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.ChatJoinRequestQueryId
import dev.inmo.tgbotapi.types.chatJoinRequestQueryIdField
import dev.inmo.tgbotapi.types.resultField
import dev.inmo.tgbotapi.utils.serializers.UnitFromBooleanSerializer
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
* Result of an [AnswerChatJoinRequestQuery]. Serialized as a plain string.
*
* @see <a href="https://core.telegram.org/bots/api#answerchatjoinrequestquery">answerChatJoinRequestQuery</a>
*/
@Serializable(ChatJoinRequestQueryResult.Companion::class)
sealed interface ChatJoinRequestQueryResult {
val name: String
/**
* Allow the user to join the chat.
*/
@Serializable(ChatJoinRequestQueryResult.Companion::class)
data object Approve : ChatJoinRequestQueryResult {
override val name: String = "approve"
}
/**
* Disallow the user to join the chat.
*/
@Serializable(ChatJoinRequestQueryResult.Companion::class)
data object Decline : ChatJoinRequestQueryResult {
override val name: String = "decline"
}
/**
* Leave the decision to other administrators.
*/
@Serializable(ChatJoinRequestQueryResult.Companion::class)
data object Queue : ChatJoinRequestQueryResult {
override val name: String = "queue"
}
/**
* Any other result which is currently unknown to this library.
*/
@Serializable(ChatJoinRequestQueryResult.Companion::class)
data class Unknown(override val name: String) : ChatJoinRequestQueryResult
companion object : KSerializer<ChatJoinRequestQueryResult> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("ChatJoinRequestQueryResult", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: ChatJoinRequestQueryResult) {
encoder.encodeString(value.name)
}
override fun deserialize(decoder: Decoder): ChatJoinRequestQueryResult {
return when (val name = decoder.decodeString()) {
Approve.name -> Approve
Decline.name -> Decline
Queue.name -> Queue
else -> Unknown(name)
}
}
}
}
/**
* Use this method to process a received chat join request query.
*
* @see <a href="https://core.telegram.org/bots/api#answerchatjoinrequestquery">answerChatJoinRequestQuery</a>
*/
@Serializable
data class AnswerChatJoinRequestQuery(
@SerialName(chatJoinRequestQueryIdField)
val chatJoinRequestQueryId: ChatJoinRequestQueryId,
@SerialName(resultField)
val result: ChatJoinRequestQueryResult
) : SimpleRequest<Unit> {
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override val resultDeserializer: DeserializationStrategy<Unit>
get() = UnitFromBooleanSerializer
override fun method(): String = "answerChatJoinRequestQuery"
}

View File

@@ -0,0 +1,34 @@
package dev.inmo.tgbotapi.requests.chat.invite_links
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.ChatJoinRequestQueryId
import dev.inmo.tgbotapi.types.chatJoinRequestQueryIdField
import dev.inmo.tgbotapi.types.webAppUrlField
import dev.inmo.tgbotapi.utils.serializers.UnitFromBooleanSerializer
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationStrategy
/**
* Use this method to process a received chat join request query by showing a Mini App to the user before deciding the
* outcome. Call [AnswerChatJoinRequestQuery] to resolve the join request query based on the user interaction with the
* Mini App.
*
* @see <a href="https://core.telegram.org/bots/api#sendchatjoinrequestwebapp">sendChatJoinRequestWebApp</a>
*/
@Serializable
data class SendChatJoinRequestWebApp(
@SerialName(chatJoinRequestQueryIdField)
val chatJoinRequestQueryId: ChatJoinRequestQueryId,
@SerialName(webAppUrlField)
val webAppUrl: String
) : SimpleRequest<Unit> {
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override val resultDeserializer: DeserializationStrategy<Unit>
get() = UnitFromBooleanSerializer
override fun method(): String = "sendChatJoinRequestWebApp"
}

View File

@@ -3,5 +3,5 @@ package dev.inmo.tgbotapi.requests.edit.abstracts
import dev.inmo.tgbotapi.abstracts.TextedOutput
interface EditTextChatMessage : TextedOutput {
override val text: String
override val text: String?
}

View File

@@ -13,6 +13,7 @@ import dev.inmo.tgbotapi.types.message.RawMessageEntity
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.toRawMessageEntities
import dev.inmo.tgbotapi.types.rich.InputRichMessage
import dev.inmo.tgbotapi.utils.extensions.makeString
import kotlinx.serialization.*
@@ -55,6 +56,24 @@ fun EditChatMessageText(
replyMarkup
)
fun EditChatMessageRichText(
chatId: ChatIdentifier,
messageId: MessageId,
richMessage: InputRichMessage,
businessConnectionId: BusinessConnectionId? = chatId.businessConnectionId,
replyMarkup: InlineKeyboardMarkup? = null
) = EditChatMessageText(
chatId = chatId,
messageId = messageId,
text = null,
parseMode = null,
rawEntities = null,
businessConnectionId = businessConnectionId,
linkPreviewOptions = null,
replyMarkup = replyMarkup,
richMessage = richMessage
)
@ConsistentCopyVisibility
@Serializable
data class EditChatMessageText internal constructor(
@@ -63,7 +82,7 @@ data class EditChatMessageText internal constructor(
@SerialName(messageIdField)
override val messageId: MessageId,
@SerialName(textField)
override val text: String,
override val text: String? = null,
@SerialName(parseModeField)
override val parseMode: ParseMode? = null,
@SerialName(entitiesField)
@@ -73,10 +92,12 @@ data class EditChatMessageText internal constructor(
@SerialName(linkPreviewOptionsField)
override val linkPreviewOptions: LinkPreviewOptions? = null,
@SerialName(replyMarkupField)
override val replyMarkup: InlineKeyboardMarkup? = null
override val replyMarkup: InlineKeyboardMarkup? = null,
@SerialName(richMessageField)
val richMessage: InputRichMessage? = null
) : EditChatMessage<TextContent>, EditTextChatMessage, EditReplyMessage, EditLinkPreviewOptionsContainer {
override val textSources: TextSourcesList? by lazy {
rawEntities ?.asTextSources(text)
text ?.let { rawEntities ?.asTextSources(it) }
}
override fun method(): String = editMessageTextMethod

View File

@@ -0,0 +1,65 @@
package dev.inmo.tgbotapi.requests.send
import dev.inmo.tgbotapi.requests.send.abstracts.ReplyingMarkupSendMessageRequest
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.message.*
import dev.inmo.tgbotapi.types.message.abstracts.ChatContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializationStrategyClass
import dev.inmo.tgbotapi.types.message.content.RichMessageContent
import dev.inmo.tgbotapi.types.rich.InputRichMessage
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationStrategy
internal val RichMessageContentMessageResultDeserializer: DeserializationStrategy<ChatContentMessage<RichMessageContent>>
= TelegramBotAPIMessageDeserializationStrategyClass()
/**
* Use this method to send rich messages.
*
* @see <a href="https://core.telegram.org/bots/api#sendrichmessage">sendRichMessage</a>
*/
@Serializable
data class SendRichMessage(
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(richMessageField)
val richMessage: InputRichMessage,
@OptIn(ExperimentalSerializationApi::class)
@SerialName(messageThreadIdField)
@EncodeDefault
override val threadId: MessageThreadId? = chatId.threadId,
@OptIn(ExperimentalSerializationApi::class)
@EncodeDefault
@SerialName(directMessagesTopicIdField)
override val directMessageThreadId: DirectMessageThreadId? = chatId.directMessageThreadId,
@SerialName(businessConnectionIdField)
override val businessConnectionId: BusinessConnectionId? = chatId.businessConnectionId,
@SerialName(disableNotificationField)
override val disableNotification: Boolean = false,
@SerialName(protectContentField)
override val protectContent: Boolean = false,
@SerialName(allowPaidBroadcastField)
override val allowPaidBroadcast: Boolean = false,
@SerialName(messageEffectIdField)
override val effectId: EffectId? = null,
@SerialName(suggestedPostParametersField)
override val suggestedPostParameters: SuggestedPostParameters? = null,
@SerialName(replyParametersField)
override val replyParameters: ReplyParameters? = null,
@SerialName(replyMarkupField)
override val replyMarkup: KeyboardMarkup? = null
) : SendContentMessageRequest<ChatContentMessage<RichMessageContent>>,
ReplyingMarkupSendMessageRequest<ChatContentMessage<RichMessageContent>> {
override fun method(): String = "sendRichMessage"
override val resultDeserializer: DeserializationStrategy<ChatContentMessage<RichMessageContent>>
get() = RichMessageContentMessageResultDeserializer
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}

View File

@@ -0,0 +1,51 @@
package dev.inmo.tgbotapi.requests.send
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.chatIdField
import dev.inmo.tgbotapi.types.draftIdField
import dev.inmo.tgbotapi.types.messageThreadIdField
import dev.inmo.tgbotapi.types.richMessageField
import dev.inmo.tgbotapi.types.rich.InputRichMessage
import dev.inmo.tgbotapi.utils.serializers.UnitFromBooleanSerializer
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationStrategy
/**
* Use this method to stream a partial rich message to a user while the message is being generated. The streamed draft is
* 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.
*
* @see <a href="https://core.telegram.org/bots/api#sendrichmessagedraft">sendRichMessageDraft</a>
*/
@Serializable
data class SendRichMessageDraft(
@SerialName(chatIdField)
val chatId: ChatId,
/**
* Unique identifier of the message draft; must be non-zero. Changes to drafts with the same identifier are animated.
*/
@SerialName(draftIdField)
val draftId: Long,
@SerialName(richMessageField)
val richMessage: InputRichMessage,
@SerialName(messageThreadIdField)
val threadId: MessageThreadId? = chatId.threadId
) : SimpleRequest<Unit> {
init {
require(draftId != 0L) {
"draftId of SendRichMessageDraft must be non-zero"
}
}
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override val resultDeserializer: DeserializationStrategy<Unit>
get() = UnitFromBooleanSerializer
override fun method(): String = "sendRichMessageDraft"
}

View File

@@ -0,0 +1,19 @@
package dev.inmo.tgbotapi.types
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
/**
* Identifier of a join request query.
*
* @see <a href="https://core.telegram.org/bots/api#chatjoinrequest">ChatJoinRequest.query_id</a>
*/
@Serializable
@JvmInline
value class ChatJoinRequestQueryId(
val string: String
) {
override fun toString(): String {
return string
}
}

View File

@@ -463,6 +463,7 @@ const val inputMessageContentField = "input_message_content"
const val hideUrlField = "hide_url"
const val botCommandField = "command"
const val botCommandFullField = "bot_command"
const val botCommandsField = "commands"
const val scopeField = "scope"
@@ -556,6 +557,7 @@ const val stickerField = "sticker"
const val oldStickerField = "old_sticker"
const val keywordsField = "keywords"
const val urlField = "url"
const val linkField = "link"
const val addressField = "address"
const val actionField = "action"
const val positionField = "position"
@@ -567,6 +569,45 @@ const val payloadField = "payload"
const val vcardField = "vcard"
const val resultsField = "results"
const val resultField = "result"
const val guardBotField = "guard_bot"
const val supportsJoinRequestQueriesField = "supports_join_request_queries"
const val queryIdField = "query_id"
const val chatJoinRequestQueryIdField = "chat_join_request_query_id"
const val webAppUrlField = "web_app_url"
const val richMessageField = "rich_message"
const val isRtlField = "is_rtl"
const val skipEntityDetectionField = "skip_entity_detection"
const val markdownField = "markdown"
const val htmlField = "html"
const val unixTimeField = "unix_time"
const val dateTimeFormatField = "date_time_format"
const val alternativeTextField = "alternative_text"
const val expressionField = "expression"
const val emailAddressField = "email_address"
const val hashtagField = "hashtag"
const val cashtagField = "cashtag"
const val bankCardNumberField = "bank_card_number"
const val anchorNameField = "anchor_name"
const val referenceNameField = "reference_name"
const val blocksField = "blocks"
const val itemsField = "items"
const val summaryField = "summary"
const val sizeField = "size"
const val languageField = "language"
const val creditField = "credit"
const val cellsField = "cells"
const val isHeaderField = "is_header"
const val colspanField = "colspan"
const val rowspanField = "rowspan"
const val alignField = "align"
const val valignField = "valign"
const val zoomField = "zoom"
const val voiceNoteField = "voice_note"
const val hasCheckboxField = "has_checkbox"
const val isCheckedField = "is_checked"
const val isOpenField = "is_open"
const val isBorderedField = "is_bordered"
const val isStripedField = "is_striped"
const val certificateField = "certificate"
const val questionField = "question"
const val questionEntitiesField = "question_entities"

View File

@@ -0,0 +1,17 @@
package dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent
import dev.inmo.tgbotapi.types.richMessageField
import dev.inmo.tgbotapi.types.rich.InputRichMessage
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Represents the content of a rich message to be sent as the result of an inline query.
*
* @see <a href="https://core.telegram.org/bots/api#inputrichmessagecontent">InputRichMessageContent</a>
*/
@Serializable
data class InputRichMessageContent(
@SerialName(richMessageField)
val richMessage: InputRichMessage
) : InputMessageContent

View File

@@ -22,6 +22,7 @@ object InputMessageContentSerializer : KSerializer<InputMessageContent> {
is InputTextMessageContent -> InputTextMessageContent.serializer().serialize(encoder, value)
is InputVenueMessageContent -> InputVenueMessageContent.serializer().serialize(encoder, value)
is InputInvoiceMessageContent -> InputInvoiceMessageContent.serializer().serialize(encoder, value)
is InputRichMessageContent -> InputRichMessageContent.serializer().serialize(encoder, value)
}
}

View File

@@ -0,0 +1,16 @@
package dev.inmo.tgbotapi.types
import dev.inmo.tgbotapi.types.media.PollMedia
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Represents an HTTP link.
*
* @see <a href="https://core.telegram.org/bots/api#link">Link</a>
*/
@Serializable
data class Link(
@SerialName(urlField)
val url: String
) : PollMedia

View File

@@ -24,7 +24,9 @@ data class ChatJoinRequest(
@SerialName(inviteLinkField)
val inviteLink: ChatInviteLink? = null,
@SerialName(bioField)
val bio: String? = null
val bio: String? = null,
@SerialName(queryIdField)
val queryId: ChatJoinRequestQueryId? = null
) : FromUser {
@Suppress("unused")
val dateTime: DateTime

View File

@@ -69,7 +69,9 @@ data class ExtendedChannelChatImpl(
@SerialName(maxReactionCountField)
override val maxReactionsCount: Int = 3,
@SerialName(uniqueGiftColorsField)
override val uniqueGiftColors: UniqueGiftColors? = null
override val uniqueGiftColors: UniqueGiftColors? = null,
@SerialName(guardBotField)
override val guardBot: User? = null
) : ExtendedChannelChat
@Serializable
@@ -116,7 +118,9 @@ data class ExtendedGroupChatImpl(
@SerialName(paidMessageStarCountField)
override val paidMessageStarCount: Int? = null,
@SerialName(uniqueGiftColorsField)
override val uniqueGiftColors: UniqueGiftColors? = null
override val uniqueGiftColors: UniqueGiftColors? = null,
@SerialName(guardBotField)
override val guardBot: User? = null
) : ExtendedGroupChat
@Serializable
@@ -316,7 +320,9 @@ data class ExtendedSupergroupChatImpl(
@SerialName(paidMessageStarCountField)
override val paidMessageStarCount: Int? = null,
@SerialName(uniqueGiftColorsField)
override val uniqueGiftColors: UniqueGiftColors? = null
override val uniqueGiftColors: UniqueGiftColors? = null,
@SerialName(guardBotField)
override val guardBot: User? = null
) : ExtendedSupergroupChat
@Serializable
@@ -390,7 +396,9 @@ data class ExtendedForumChatImpl(
@SerialName(paidMessageStarCountField)
override val paidMessageStarCount: Int? = null,
@SerialName(uniqueGiftColorsField)
override val uniqueGiftColors: UniqueGiftColors? = null
override val uniqueGiftColors: UniqueGiftColors? = null,
@SerialName(guardBotField)
override val guardBot: User? = null
) : ExtendedForumChat
@Serializable
@@ -467,7 +475,9 @@ data class ExtendedChannelDirectMessagesChatImpl(
@SerialName(paidMessageStarCountField)
override val paidMessageStarCount: Int? = null,
@SerialName(uniqueGiftColorsField)
override val uniqueGiftColors: UniqueGiftColors? = null
override val uniqueGiftColors: UniqueGiftColors? = null,
@SerialName(guardBotField)
override val guardBot: User? = null
) : ExtendedChannelDirectMessagesChat {
@OptIn(ExperimentalSerializationApi::class)
@SerialName(isDirectMessagesField)
@@ -495,6 +505,8 @@ data class ExtendedBot(
val supportsInlineQueries: Boolean = false,
@SerialName(supportsGuestQueriesField)
val supportsGuestQueries: Boolean = false,
@SerialName(supportsJoinRequestQueriesField)
val supportsJoinRequestQueries: Boolean = false,
@SerialName(canConnectToBusinessField)
val canConnectToBusiness: Boolean = false,
@SerialName(photoField)

View File

@@ -27,6 +27,14 @@ sealed interface ExtendedChat : Chat {
val uniqueGiftColors: UniqueGiftColors?
/**
* The bot that processes join request queries in the chat. The field is only available to chat administrators.
*
* @see <a href="https://core.telegram.org/bots/api#chatfullinfo">ChatFullInfo.guard_bot</a>
*/
val guardBot: User?
get() = null
@Deprecated(
message = "Telegram Bot API v9.0 introduced the new field, `acceptedGiftTypes`, to allow granular" +
" control over which types of gifts user, bot, or chat can accept.",

View File

@@ -27,6 +27,7 @@ object InputPollOptionMediaSerializer : KSerializer<InputPollOptionMedia> {
override fun serialize(encoder: Encoder, value: InputPollOptionMedia) {
when (value) {
is TelegramMediaAnimation -> TelegramMediaAnimation.serializer().serialize(encoder, value)
is TelegramMediaLink -> TelegramMediaLink.serializer().serialize(encoder, value)
is TelegramMediaLivePhoto -> TelegramMediaLivePhoto.serializer().serialize(encoder, value)
is TelegramMediaLocation -> TelegramMediaLocation.serializer().serialize(encoder, value)
is TelegramMediaPhoto -> TelegramMediaPhoto.serializer().serialize(encoder, value)

View File

@@ -10,6 +10,8 @@ import dev.inmo.tgbotapi.types.files.LivePhotoFile
import dev.inmo.tgbotapi.types.files.PhotoFile
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.files.VideoFile
import dev.inmo.tgbotapi.types.Link
import dev.inmo.tgbotapi.types.linkField
import dev.inmo.tgbotapi.types.livePhotoField
import dev.inmo.tgbotapi.types.location.StaticLocation
import dev.inmo.tgbotapi.types.locationField
@@ -36,6 +38,8 @@ interface PollMedia : BaseTelegramMediaFile {
val audio: AudioFile? = null,
@SerialName(documentField)
val document: DocumentFile? = null,
@SerialName(linkField)
val link: Link? = null,
@SerialName(livePhotoField)
val livePhoto: LivePhotoFile? = null,
@SerialName(photoField)
@@ -61,6 +65,7 @@ interface PollMedia : BaseTelegramMediaFile {
surrogate.animation != null -> surrogate.animation
surrogate.audio != null -> surrogate.audio
surrogate.document != null -> surrogate.document
surrogate.link != null -> surrogate.link
surrogate.livePhoto != null -> surrogate.livePhoto
surrogate.photo != null -> surrogate.photo
surrogate.sticker != null -> surrogate.sticker
@@ -76,6 +81,7 @@ interface PollMedia : BaseTelegramMediaFile {
animation = value as? AnimationFile,
audio = value as? AudioFile,
document = value as? DocumentFile,
link = value as? Link,
livePhoto = value as? LivePhotoFile,
photo = value as? PhotoFile,
sticker = value as? Sticker,

View File

@@ -0,0 +1,26 @@
package dev.inmo.tgbotapi.types.media
import dev.inmo.tgbotapi.types.typeField
import dev.inmo.tgbotapi.types.urlField
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Represents an HTTP link to be sent. Can be used as [InputPollOptionMedia].
*
* @see <a href="https://core.telegram.org/bots/api#inputmedialink">InputMediaLink</a>
*/
@Serializable
data class TelegramMediaLink(
@SerialName(urlField)
val url: String,
) : InputPollOptionMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
companion object {
const val TYPE = "link"
}
}

View File

@@ -50,6 +50,7 @@ import dev.inmo.tgbotapi.types.polls.Poll
import dev.inmo.tgbotapi.types.polls.PollOptionPersistentId
import dev.inmo.tgbotapi.types.request.ChatShared
import dev.inmo.tgbotapi.types.request.UsersShared
import dev.inmo.tgbotapi.types.rich.RichTextInfo
import dev.inmo.tgbotapi.types.stories.Story
import dev.inmo.tgbotapi.types.venue.Venue
import dev.inmo.tgbotapi.utils.isFakeTelegramUser
@@ -92,6 +93,7 @@ internal data class RawMessage(
private val caption_entities: RawMessageEntities? = null,
private val has_media_spoiler: Boolean? = null,
private val story: Story? = null,
private val rich_message: RichTextInfo? = null,
private val audio: AudioFile? = null,
private val document: DocumentFile? = null,
private val paid_media: PaidMediaInfo? = null,
@@ -230,6 +232,11 @@ internal data class RawMessage(
} ?: emptyList()
when {
rich_message != null -> RichMessageContent(
chat,
messageId,
rich_message
)
story != null -> StoryContent(
chat,
messageId,

View File

@@ -55,6 +55,7 @@ sealed interface MessageContent: ResendableContent {
subclass(StoryContent::class)
subclass(GiveawayPublicResultsContent::class)
subclass(GiveawayContent::class)
subclass(RichMessageContent::class)
additionalBuilder()
}

View File

@@ -0,0 +1,78 @@
package dev.inmo.tgbotapi.types.message.content
import dev.inmo.tgbotapi.requests.ForwardMessage
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.requests.send.CopyMessage
import dev.inmo.tgbotapi.requests.send.SendRichMessage
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.chat.Chat
import dev.inmo.tgbotapi.types.message.SuggestedPostParameters
import dev.inmo.tgbotapi.types.message.abstracts.ChatContentMessage
import dev.inmo.tgbotapi.types.rich.InputRichMessageMarkdown
import dev.inmo.tgbotapi.types.rich.RichBlock
import dev.inmo.tgbotapi.types.rich.RichBlockAudio
import dev.inmo.tgbotapi.types.rich.RichBlockMedia
import dev.inmo.tgbotapi.types.rich.RichBlockPhoto
import dev.inmo.tgbotapi.types.rich.RichBlockVideo
import dev.inmo.tgbotapi.types.rich.RichTextInfo
import dev.inmo.tgbotapi.types.rich.markdown
import dev.inmo.tgbotapi.types.rich.search
import kotlinx.serialization.Serializable
@Serializable
data class RichMessageContent(
private val chat: Chat,
private val messageId: MessageId,
val richMessage: RichTextInfo
) : MessageContent {
override fun createResend(
chatId: ChatIdentifier,
messageThreadId: MessageThreadId?,
directMessageThreadId: DirectMessageThreadId?,
businessConnectionId: BusinessConnectionId?,
disableNotification: Boolean,
protectContent: Boolean,
allowPaidBroadcast: Boolean,
effectId: EffectId?,
suggestedPostParameters: SuggestedPostParameters?,
replyParameters: ReplyParameters?,
replyMarkup: KeyboardMarkup?
): Request<ChatContentMessage<RichMessageContent>> {
val isThereMedia = richMessage.blocks.any {
it.search {
this is RichBlockMedia
} != null
}
return if (isThereMedia) {
@Suppress("UNCHECKED_CAST")
ForwardMessage(
chat.id,
toChatId = chatId,
messageId = messageId,
threadId = messageThreadId,
directMessageThreadId = directMessageThreadId,
disableNotification = disableNotification,
protectContent = protectContent,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
) as Request<ChatContentMessage<RichMessageContent>>
} else {
SendRichMessage(
chatId = chatId,
richMessage = InputRichMessageMarkdown(richMessage.markdown, isRtl = richMessage.isRtl),
threadId = messageThreadId,
directMessageThreadId = directMessageThreadId,
businessConnectionId = businessConnectionId,
disableNotification = disableNotification,
protectContent = protectContent,
allowPaidBroadcast = allowPaidBroadcast,
effectId = effectId,
suggestedPostParameters = suggestedPostParameters,
replyParameters = replyParameters,
replyMarkup = replyMarkup
)
}
}
}

View File

@@ -13,6 +13,8 @@ typealias PollMessage = ChatContentMessage<PollContent>
typealias TextMessage = ChatContentMessage<TextContent>
typealias StoryMessage = ChatContentMessage<StoryContent>
typealias RichMessageContentMessage = ChatContentMessage<RichMessageContent>
typealias LocationMessage = ChatContentMessage<LocationContent>
typealias LiveLocationMessage = ChatContentMessage<LiveLocationContent>
typealias StaticLocationMessage = ChatContentMessage<StaticLocationContent>

View File

@@ -0,0 +1,65 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.types.htmlField
import dev.inmo.tgbotapi.types.isRtlField
import dev.inmo.tgbotapi.types.markdownField
import dev.inmo.tgbotapi.types.skipEntityDetectionField
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Describes a rich message to be sent. Exactly one of the fields [html] or [markdown] must be used. Use the
* [InputRichMessageHTML] and [InputRichMessageMarkdown] factories to build an instance.
*
* @see <a href="https://core.telegram.org/bots/api#inputrichmessage">InputRichMessage</a>
*/
@ConsistentCopyVisibility
@Serializable
data class InputRichMessage internal constructor(
@SerialName(htmlField)
val html: String? = null,
@SerialName(markdownField)
val markdown: String? = null,
@SerialName(isRtlField)
val isRtl: Boolean? = null,
@SerialName(skipEntityDetectionField)
val skipEntityDetection: Boolean? = null
) {
init {
require((html == null) != (markdown == null)) {
"Exactly one of the fields html or markdown must be used in InputRichMessage"
}
}
}
/**
* Creates an [InputRichMessage] with the content described using HTML formatting.
*
* @see <a href="https://core.telegram.org/bots/api#inputrichmessage">InputRichMessage</a>
*/
fun InputRichMessageHTML(
html: String,
isRtl: Boolean? = null,
skipEntityDetection: Boolean? = null
): InputRichMessage = InputRichMessage(
html = html,
markdown = null,
isRtl = isRtl,
skipEntityDetection = skipEntityDetection
)
/**
* Creates an [InputRichMessage] with the content described using Markdown formatting.
*
* @see <a href="https://core.telegram.org/bots/api#inputrichmessage">InputRichMessage</a>
*/
fun InputRichMessageMarkdown(
markdown: String,
isRtl: Boolean? = null,
skipEntityDetection: Boolean? = null
): InputRichMessage = InputRichMessage(
html = null,
markdown = markdown,
isRtl = isRtl,
skipEntityDetection = skipEntityDetection
)

View File

@@ -0,0 +1,95 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.files.TelegramMediaFile
import dev.inmo.tgbotapi.types.typeField
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
/**
* Represents a block in a rich formatted message.
*
* @see <a href="https://core.telegram.org/bots/api#richblock">RichBlock</a>
*/
@Serializable(RichBlockSerializer::class)
@ClassCastsIncluded
sealed interface RichBlock {
val type: String
/**
* [Rich Markdown style](https://core.telegram.org/bots/api#rich-markdown-style) source of this single [RichBlock].
*/
val markdown: String
/**
* [Rich HTML style](https://core.telegram.org/bots/api#rich-html-style) source of this single [RichBlock].
*/
val html: String
}
@Serializable(RichBlockSerializer::class)
sealed interface RichBlockMedia : RichBlock {
val media: TelegramMediaFile
val caption: RichBlockCaption?
}
/**
* The nested [RichBlock]s directly contained by this block, or an empty list for leaf blocks. Container blocks
* ([RichBlockList] via its [RichBlockListItem.blocks], [RichBlockBlockQuotation], [RichBlockCollage],
* [RichBlockSlideshow] and [RichBlockDetails]) expose their children here.
*/
val RichBlock.subBlocks: List<RichBlock>
get() = when (this) {
is RichBlockList -> items.flatMap { it.blocks }
is RichBlockBlockQuotation -> blocks
is RichBlockCollage -> blocks
is RichBlockSlideshow -> blocks
is RichBlockDetails -> blocks
else -> emptyList()
}
/**
* Walks this [RichBlock] and all of its [subBlocks] recursively (depth-first, this block first) and returns the first
* block for which [block] returns `true`, or `null` if none matches.
*/
fun RichBlock.search(block: RichBlock.() -> Boolean): RichBlock? {
if (block()) return this
for (child in subBlocks) {
child.search(block)?.let { return it }
}
return null
}
object RichBlockSerializer : JsonContentPolymorphicSerializer<RichBlock>(RichBlock::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<RichBlock> {
return when (val type = element.jsonObject[typeField]?.jsonPrimitive?.content) {
RichBlockParagraph.TYPE -> RichBlockParagraph.serializer()
RichBlockSectionHeading.TYPE -> RichBlockSectionHeading.serializer()
RichBlockPreformatted.TYPE -> RichBlockPreformatted.serializer()
RichBlockFooter.TYPE -> RichBlockFooter.serializer()
RichBlockDivider.TYPE -> RichBlockDivider.serializer()
RichBlockMathematicalExpression.TYPE -> RichBlockMathematicalExpression.serializer()
RichBlockAnchor.TYPE -> RichBlockAnchor.serializer()
RichBlockList.TYPE -> RichBlockList.serializer()
RichBlockBlockQuotation.TYPE -> RichBlockBlockQuotation.serializer()
RichBlockPullQuotation.TYPE -> RichBlockPullQuotation.serializer()
RichBlockCollage.TYPE -> RichBlockCollage.serializer()
RichBlockSlideshow.TYPE -> RichBlockSlideshow.serializer()
RichBlockTable.TYPE -> RichBlockTable.serializer()
RichBlockDetails.TYPE -> RichBlockDetails.serializer()
RichBlockMap.TYPE -> RichBlockMap.serializer()
RichBlockAnimation.TYPE -> RichBlockAnimation.serializer()
RichBlockAudio.TYPE -> RichBlockAudio.serializer()
RichBlockPhoto.TYPE -> RichBlockPhoto.serializer()
RichBlockVideo.TYPE -> RichBlockVideo.serializer()
RichBlockVoiceNote.TYPE -> RichBlockVoiceNote.serializer()
RichBlockThinking.TYPE -> RichBlockThinking.serializer()
else -> error("Unknown RichBlock type: $type")
}
}
}

View File

@@ -0,0 +1,54 @@
package dev.inmo.tgbotapi.types.rich
/**
* Builds the [Rich Markdown style](https://core.telegram.org/bots/api#rich-markdown-style) source string for this list
* of [RichBlock]s. The resulting string may be passed to [InputRichMessageMarkdown].
*
* Media blocks ([RichBlockPhoto], [RichBlockVideo], [RichBlockAudio], [RichBlockVoiceNote] and [RichBlockAnimation]) are
* rendered using the Telegram file_id as the media source. Telegram only accepts HTTP(S) URLs for rich media, so for
* those blocks the output is a faithful structural representation rather than a directly sendable message.
*/
fun List<RichBlock>.toRichMarkdown(): String = joinToString(separator = "\n\n") { it.markdown }
/**
* Builds the [Rich HTML style](https://core.telegram.org/bots/api#rich-html-style) source string for this list of
* [RichBlock]s. The resulting string may be passed to [InputRichMessageHTML]. See [toRichMarkdown] for the media note.
*/
fun List<RichBlock>.toRichHtml(): String = joinToString(separator = "\n") { it.html }
/**
* [Rich Markdown style](https://core.telegram.org/bots/api#rich-markdown-style) source of all the [RichTextInfo.blocks].
*/
val RichTextInfo.markdown: String
get() = blocks.toRichMarkdown()
/**
* [Rich HTML style](https://core.telegram.org/bots/api#rich-html-style) source of all the [RichTextInfo.blocks].
*/
val RichTextInfo.html: String
get() = blocks.toRichHtml()
internal fun creditCiteMarkdown(credit: RichText?): String = credit?.let { "<cite>${it.markdown}</cite>" } ?: ""
internal fun creditCiteHtml(credit: RichText?): String = credit?.let { "<cite>${it.html}</cite>" } ?: ""
internal fun richMediaContainerMarkdown(tag: String, blocks: List<RichBlock>, caption: RichBlockCaption?): String {
val media = blocks.joinToString(separator = "\n") { it.markdown }
val captionPart = caption?.let { "\n<figcaption>${it.text.markdown}${creditCiteMarkdown(it.credit)}</figcaption>" } ?: ""
return "<$tag>\n\n$media$captionPart\n\n</$tag>"
}
internal fun richMediaContainerHtml(tag: String, blocks: List<RichBlock>, caption: RichBlockCaption?): String {
val media = blocks.joinToString(separator = "") { it.html }
val captionPart = caption?.let { "<figcaption>${it.text.html}${creditCiteHtml(it.credit)}</figcaption>" } ?: ""
return "<$tag>$media$captionPart</$tag>"
}
internal fun richMediaMarkdown(source: String, caption: RichBlockCaption?): String =
caption?.let { "![](" + source + " \"" + it.text.rawText + "\")" } ?: "![]($source)"
internal fun richMediaHtml(tag: String, source: String, spoiler: Boolean, selfClosing: Boolean, caption: RichBlockCaption?): String {
val spoilerAttribute = if (spoiler) " tg-spoiler" else ""
val element = if (selfClosing) "<$tag src=\"$source\"$spoilerAttribute/>" else "<$tag src=\"$source\"$spoilerAttribute></$tag>"
return caption?.let { "<figure>$element<figcaption>${it.text.html}${creditCiteHtml(it.credit)}</figcaption></figure>" } ?: element
}

View File

@@ -0,0 +1,75 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.types.alignField
import dev.inmo.tgbotapi.types.blocksField
import dev.inmo.tgbotapi.types.colspanField
import dev.inmo.tgbotapi.types.creditField
import dev.inmo.tgbotapi.types.hasCheckboxField
import dev.inmo.tgbotapi.types.isCheckedField
import dev.inmo.tgbotapi.types.isHeaderField
import dev.inmo.tgbotapi.types.labelField
import dev.inmo.tgbotapi.types.rowspanField
import dev.inmo.tgbotapi.types.textField
import dev.inmo.tgbotapi.types.typeField
import dev.inmo.tgbotapi.types.valignField
import dev.inmo.tgbotapi.types.valueField
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Caption of a rich formatted block.
*
* @see <a href="https://core.telegram.org/bots/api#richblockcaption">RichBlockCaption</a>
*/
@Serializable
data class RichBlockCaption(
@SerialName(textField)
val text: RichText,
@SerialName(creditField)
val credit: RichText? = null
)
/**
* A cell in a [RichBlockTable].
*
* @see <a href="https://core.telegram.org/bots/api#richblocktablecell">RichBlockTableCell</a>
*/
@Serializable
data class RichBlockTableCell(
@SerialName(textField)
val text: RichText? = null,
@SerialName(isHeaderField)
val isHeader: Boolean? = null,
@SerialName(colspanField)
val colspan: Int? = null,
@SerialName(rowspanField)
val rowspan: Int? = null,
@SerialName(alignField)
val align: String,
@SerialName(valignField)
val valign: String
)
/**
* An item of a [RichBlockList].
*
* @see <a href="https://core.telegram.org/bots/api#richblocklistitem">RichBlockListItem</a>
*/
@Serializable
data class RichBlockListItem(
@SerialName(labelField)
val label: String,
@SerialName(blocksField)
val blocks: List<RichBlock>,
@SerialName(hasCheckboxField)
val hasCheckbox: Boolean? = null,
@SerialName(isCheckedField)
val isChecked: Boolean? = null,
@SerialName(valueField)
val value: Int? = null,
/**
* For ordered lists, the type of the item label; must be one of "a", "A", "i", "I" or "1".
*/
@SerialName(typeField)
val labelType: String? = null
)

View File

@@ -0,0 +1,708 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.types.animationField
import dev.inmo.tgbotapi.types.audioField
import dev.inmo.tgbotapi.types.blocksField
import dev.inmo.tgbotapi.types.captionField
import dev.inmo.tgbotapi.types.cellsField
import dev.inmo.tgbotapi.types.creditField
import dev.inmo.tgbotapi.types.expressionField
import dev.inmo.tgbotapi.types.files.AnimationFile
import dev.inmo.tgbotapi.types.files.AudioFile
import dev.inmo.tgbotapi.types.files.PhotoFile
import dev.inmo.tgbotapi.types.files.TelegramMediaFile
import dev.inmo.tgbotapi.types.files.VideoFile
import dev.inmo.tgbotapi.types.files.VoiceFile
import dev.inmo.tgbotapi.types.hasSpoilerField
import dev.inmo.tgbotapi.types.heightField
import dev.inmo.tgbotapi.types.isBorderedField
import dev.inmo.tgbotapi.types.isOpenField
import dev.inmo.tgbotapi.types.isStripedField
import dev.inmo.tgbotapi.types.itemsField
import dev.inmo.tgbotapi.types.languageField
import dev.inmo.tgbotapi.types.location.StaticLocation
import dev.inmo.tgbotapi.types.locationField
import dev.inmo.tgbotapi.types.nameField
import dev.inmo.tgbotapi.types.photoField
import dev.inmo.tgbotapi.types.sizeField
import dev.inmo.tgbotapi.types.summaryField
import dev.inmo.tgbotapi.types.textField
import dev.inmo.tgbotapi.types.typeField
import dev.inmo.tgbotapi.types.videoField
import dev.inmo.tgbotapi.types.voiceNoteField
import dev.inmo.tgbotapi.types.widthField
import dev.inmo.tgbotapi.types.zoomField
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* A text paragraph.
*
* @see <a href="https://core.telegram.org/bots/api#richblockparagraph">RichBlockParagraph</a>
*/
@Serializable
data class RichBlockParagraph(
@SerialName(textField)
val text: RichText
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "paragraph"
fun markdown(text: RichText): String = text.markdown
fun html(text: RichText): String = "<p>${text.html}</p>"
}
}
/**
* A section heading.
*
* @see <a href="https://core.telegram.org/bots/api#richblocksectionheading">RichBlockSectionHeading</a>
*/
@Serializable
data class RichBlockSectionHeading(
@SerialName(textField)
val text: RichText,
/**
* Relative size of the text font; 1-6, 1 is the largest, 6 is the smallest.
*/
@SerialName(sizeField)
val level: Int
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(text, level)
override val html: String = html(text, level)
companion object {
const val TYPE = "heading"
fun markdown(text: RichText, size: Int): String = "#".repeat(size) + " " + text.markdown
fun html(text: RichText, size: Int): String = "<h$size>${text.html}</h$size>"
}
}
/**
* A preformatted text block.
*
* @see <a href="https://core.telegram.org/bots/api#richblockpreformatted">RichBlockPreformatted</a>
*/
@Serializable
data class RichBlockPreformatted(
@SerialName(textField)
val text: RichText,
@SerialName(languageField)
val language: String? = null
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(text, language)
override val html: String = html(text, language)
companion object {
const val TYPE = "pre"
fun markdown(text: RichText, language: String?): String = "```" + (language ?: "") + "\n" + text.rawText + "\n```"
fun html(text: RichText, language: String?): String =
language?.let { "<pre><code class=\"language-$it\">${text.html}</code></pre>" } ?: "<pre>${text.html}</pre>"
}
}
/**
* A footer.
*
* @see <a href="https://core.telegram.org/bots/api#richblockfooter">RichBlockFooter</a>
*/
@Serializable
data class RichBlockFooter(
@SerialName(textField)
val text: RichText
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "footer"
fun markdown(text: RichText): String = "<footer>${text.markdown}</footer>"
fun html(text: RichText): String = "<footer>${text.html}</footer>"
}
}
/**
* A divider.
*
* @see <a href="https://core.telegram.org/bots/api#richblockdivider">RichBlockDivider</a>
*/
@Serializable
class RichBlockDivider : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown()
override val html: String = html()
override fun equals(other: Any?): Boolean = other is RichBlockDivider
override fun hashCode(): Int = TYPE.hashCode()
override fun toString(): String = "RichBlockDivider"
companion object {
const val TYPE = "divider"
fun markdown(): String = "---"
fun html(): String = "<hr/>"
}
}
/**
* A block with a mathematical expression in LaTeX format.
*
* @see <a href="https://core.telegram.org/bots/api#richblockmathematicalexpression">RichBlockMathematicalExpression</a>
*/
@Serializable
data class RichBlockMathematicalExpression(
@SerialName(expressionField)
val expression: String
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(expression)
override val html: String = html(expression)
companion object {
const val TYPE = "mathematical_expression"
fun markdown(expression: String): String = "\$\$" + expression + "\$\$"
fun html(expression: String): String = "<tg-math-block>$expression</tg-math-block>"
}
}
/**
* A block with an anchor.
*
* @see <a href="https://core.telegram.org/bots/api#richblockanchor">RichBlockAnchor</a>
*/
@Serializable
data class RichBlockAnchor(
@SerialName(nameField)
val name: String
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(name)
override val html: String = html(name)
companion object {
const val TYPE = "anchor"
fun markdown(name: String): String = "<a name=\"$name\"></a>"
fun html(name: String): String = "<a name=\"$name\"></a>"
}
}
/**
* A list of blocks.
*
* @see <a href="https://core.telegram.org/bots/api#richblocklist">RichBlockList</a>
*/
@Serializable
data class RichBlockList(
@SerialName(itemsField)
val items: List<RichBlockListItem>
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(items)
override val html: String = html(items)
companion object {
const val TYPE = "list"
fun markdown(items: List<RichBlockListItem>): String =
items.mapIndexed { index, item ->
val marker = when {
item.hasCheckbox == true -> if (item.isChecked == true) "- [x] " else "- [ ] "
item.labelType != null -> "${item.value ?: (index + 1)}. "
else -> "- "
}
item.blocks.toRichMarkdown().lineSequence().mapIndexed { lineIndex, line ->
if (lineIndex == 0) "$marker$line" else " $line"
}.joinToString(separator = "\n")
}.joinToString(separator = "\n")
fun html(items: List<RichBlockListItem>): String {
val ordered = items.any { it.labelType != null }
val tag = if (ordered) "ol" else "ul"
val renderedItems = items.joinToString(separator = "") { item ->
val attributes = buildString {
item.value?.let { append(" value=\"$it\"") }
item.labelType?.let { append(" type=\"$it\"") }
}
val checkbox = if (item.hasCheckbox == true) {
"<input type=\"checkbox\"${if (item.isChecked == true) " checked" else ""}>"
} else {
""
}
"<li$attributes>$checkbox${item.blocks.toRichHtml()}</li>"
}
return "<$tag>$renderedItems</$tag>"
}
}
}
/**
* A block quotation.
*
* @see <a href="https://core.telegram.org/bots/api#richblockblockquotation">RichBlockBlockQuotation</a>
*/
@Serializable
data class RichBlockBlockQuotation(
@SerialName(blocksField)
val blocks: List<RichBlock>,
@SerialName(creditField)
val credit: RichText? = null
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(blocks, credit)
override val html: String = html(blocks, credit)
companion object {
const val TYPE = "blockquote"
fun markdown(blocks: List<RichBlock>, credit: RichText?): String {
val quoted = blocks.toRichMarkdown().lineSequence().joinToString(separator = "\n") { line ->
if (line.isEmpty()) ">" else "> $line"
}
return quoted + (credit?.let { "\n> ${creditCiteMarkdown(it)}" } ?: "")
}
fun html(blocks: List<RichBlock>, credit: RichText?): String =
"<blockquote>${blocks.toRichHtml()}${creditCiteHtml(credit)}</blockquote>"
}
}
/**
* A quotation with centered text.
*
* @see <a href="https://core.telegram.org/bots/api#richblockpullquotation">RichBlockPullQuotation</a>
*/
@Serializable
data class RichBlockPullQuotation(
@SerialName(textField)
val text: RichText,
@SerialName(creditField)
val credit: RichText? = null
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(text, credit)
override val html: String = html(text, credit)
companion object {
const val TYPE = "pullquote"
fun markdown(text: RichText, credit: RichText?): String = "<aside>${text.markdown}${creditCiteMarkdown(credit)}</aside>"
fun html(text: RichText, credit: RichText?): String = "<aside>${text.html}${creditCiteHtml(credit)}</aside>"
}
}
/**
* A collage.
*
* @see <a href="https://core.telegram.org/bots/api#richblockcollage">RichBlockCollage</a>
*/
@Serializable
data class RichBlockCollage(
@SerialName(blocksField)
val blocks: List<RichBlock>,
@SerialName(captionField)
val caption: RichBlockCaption? = null
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(blocks, caption)
override val html: String = html(blocks, caption)
companion object {
const val TYPE = "collage"
fun markdown(blocks: List<RichBlock>, caption: RichBlockCaption?): String =
richMediaContainerMarkdown("tg-collage", blocks, caption)
fun html(blocks: List<RichBlock>, caption: RichBlockCaption?): String =
richMediaContainerHtml("tg-collage", blocks, caption)
}
}
/**
* A slideshow.
*
* @see <a href="https://core.telegram.org/bots/api#richblockslideshow">RichBlockSlideshow</a>
*/
@Serializable
data class RichBlockSlideshow(
@SerialName(blocksField)
val blocks: List<RichBlock>,
@SerialName(captionField)
val caption: RichBlockCaption? = null
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(blocks, caption)
override val html: String = html(blocks, caption)
companion object {
const val TYPE = "slideshow"
fun markdown(blocks: List<RichBlock>, caption: RichBlockCaption?): String =
richMediaContainerMarkdown("tg-slideshow", blocks, caption)
fun html(blocks: List<RichBlock>, caption: RichBlockCaption?): String =
richMediaContainerHtml("tg-slideshow", blocks, caption)
}
}
/**
* A table.
*
* @see <a href="https://core.telegram.org/bots/api#richblocktable">RichBlockTable</a>
*/
@Serializable
data class RichBlockTable(
@SerialName(cellsField)
val cells: List<List<RichBlockTableCell>>,
@SerialName(isBorderedField)
val isBordered: Boolean? = null,
@SerialName(isStripedField)
val isStriped: Boolean? = null,
@SerialName(captionField)
val caption: RichText? = null
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(cells)
override val html: String = html(cells, isBordered, isStriped, caption)
companion object {
const val TYPE = "table"
fun markdown(cells: List<List<RichBlockTableCell>>): String {
if (cells.isEmpty()) return ""
fun renderRow(row: List<RichBlockTableCell>): String =
row.joinToString(separator = " | ", prefix = "| ", postfix = " |") { it.text?.markdown ?: "" }
fun alignment(cell: RichBlockTableCell): String = when (cell.align) {
"left" -> ":---"
"center" -> ":--:"
"right" -> "---:"
else -> "---"
}
val header = cells.first()
val lines = mutableListOf(
renderRow(header),
header.joinToString(separator = " | ", prefix = "| ", postfix = " |") { alignment(it) }
)
cells.drop(1).forEach { lines.add(renderRow(it)) }
return lines.joinToString(separator = "\n")
}
fun html(cells: List<List<RichBlockTableCell>>, isBordered: Boolean?, isStriped: Boolean?, caption: RichText?): String {
val attributes = buildString {
if (isBordered == true) append(" bordered")
if (isStriped == true) append(" striped")
}
val captionPart = caption?.let { "<caption>${it.html}</caption>" } ?: ""
val rows = cells.joinToString(separator = "") { row ->
val renderedCells = row.joinToString(separator = "") { cell ->
val tag = if (cell.isHeader == true) "th" else "td"
val cellAttributes = buildString {
cell.colspan?.let { append(" colspan=\"$it\"") }
cell.rowspan?.let { append(" rowspan=\"$it\"") }
append(" align=\"${cell.align}\"")
append(" valign=\"${cell.valign}\"")
}
"<$tag$cellAttributes>${cell.text?.html ?: ""}</$tag>"
}
"<tr>$renderedCells</tr>"
}
return "<table$attributes>$captionPart$rows</table>"
}
}
}
/**
* An expandable block for details disclosure.
*
* @see <a href="https://core.telegram.org/bots/api#richblockdetails">RichBlockDetails</a>
*/
@Serializable
data class RichBlockDetails(
@SerialName(summaryField)
val summary: RichText,
@SerialName(blocksField)
val blocks: List<RichBlock>,
@SerialName(isOpenField)
val isOpen: Boolean? = null
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(summary, blocks, isOpen)
override val html: String = html(summary, blocks, isOpen)
companion object {
const val TYPE = "details"
fun markdown(summary: RichText, blocks: List<RichBlock>, isOpen: Boolean?): String {
val open = if (isOpen == true) " open" else ""
return "<details$open><summary>${summary.markdown}</summary>\n\n${blocks.toRichMarkdown()}\n\n</details>"
}
fun html(summary: RichText, blocks: List<RichBlock>, isOpen: Boolean?): String {
val open = if (isOpen == true) " open" else ""
return "<details$open><summary>${summary.html}</summary>${blocks.toRichHtml()}</details>"
}
}
}
/**
* A block with a map.
*
* @see <a href="https://core.telegram.org/bots/api#richblockmap">RichBlockMap</a>
*/
@Serializable
data class RichBlockMap(
@SerialName(locationField)
val location: StaticLocation,
/**
* Map zoom level; 13-20.
*/
@SerialName(zoomField)
val zoom: Int,
@SerialName(widthField)
val width: Int,
@SerialName(heightField)
val height: Int,
@SerialName(captionField)
val caption: RichBlockCaption? = null
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(location, zoom, caption)
override val html: String = html(location, zoom, caption)
companion object {
const val TYPE = "map"
fun markdown(location: StaticLocation, zoom: Int, caption: RichBlockCaption?): String {
val element = "<tg-map lat=\"${location.latitude}\" long=\"${location.longitude}\" zoom=\"$zoom\"/>"
return caption?.let { "<figure>$element<figcaption>${it.text.markdown}${creditCiteMarkdown(it.credit)}</figcaption></figure>" } ?: element
}
fun html(location: StaticLocation, zoom: Int, caption: RichBlockCaption?): String {
val element = "<tg-map lat=\"${location.latitude}\" long=\"${location.longitude}\" zoom=\"$zoom\"/>"
return caption?.let { "<figure>$element<figcaption>${it.text.html}${creditCiteHtml(it.credit)}</figcaption></figure>" } ?: element
}
}
}
/**
* A block with an animation.
*
* @see <a href="https://core.telegram.org/bots/api#richblockanimation">RichBlockAnimation</a>
*/
@Serializable
data class RichBlockAnimation(
@SerialName(animationField)
val animation: AnimationFile,
@SerialName(hasSpoilerField)
val hasSpoiler: Boolean? = null,
@SerialName(captionField)
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(animation, caption)
override val html: String = html(animation, hasSpoiler, caption)
override val media: TelegramMediaFile
get() = animation
companion object {
const val TYPE = "animation"
fun markdown(animation: AnimationFile, caption: RichBlockCaption?): String =
richMediaMarkdown(animation.fileId.fileId, caption)
fun html(animation: AnimationFile, hasSpoiler: Boolean?, caption: RichBlockCaption?): String =
richMediaHtml("video", animation.fileId.fileId, hasSpoiler == true, selfClosing = false, caption = caption)
}
}
/**
* A block with a music file.
*
* @see <a href="https://core.telegram.org/bots/api#richblockaudio">RichBlockAudio</a>
*/
@Serializable
data class RichBlockAudio(
@SerialName(audioField)
val audio: AudioFile,
@SerialName(captionField)
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(audio, caption)
override val html: String = html(audio, caption)
override val media: TelegramMediaFile
get() = audio
companion object {
const val TYPE = "audio"
fun markdown(audio: AudioFile, caption: RichBlockCaption?): String =
richMediaMarkdown(audio.fileId.fileId, caption)
fun html(audio: AudioFile, caption: RichBlockCaption?): String =
richMediaHtml("audio", audio.fileId.fileId, spoiler = false, selfClosing = false, caption = caption)
}
}
/**
* A block with a photo.
*
* @see <a href="https://core.telegram.org/bots/api#richblockphoto">RichBlockPhoto</a>
*/
@Serializable
data class RichBlockPhoto(
@SerialName(photoField)
val photo: PhotoFile,
@SerialName(hasSpoilerField)
val hasSpoiler: Boolean? = null,
@SerialName(captionField)
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(photo, caption)
override val html: String = html(photo, hasSpoiler, caption)
override val media: TelegramMediaFile
get() = photo
companion object {
const val TYPE = "photo"
fun markdown(photo: PhotoFile, caption: RichBlockCaption?): String =
richMediaMarkdown(photo.fileId.fileId, caption)
fun html(photo: PhotoFile, hasSpoiler: Boolean?, caption: RichBlockCaption?): String =
richMediaHtml("img", photo.fileId.fileId, hasSpoiler == true, selfClosing = true, caption = caption)
}
}
/**
* A block with a video.
*
* @see <a href="https://core.telegram.org/bots/api#richblockvideo">RichBlockVideo</a>
*/
@Serializable
data class RichBlockVideo(
@SerialName(videoField)
val video: VideoFile,
@SerialName(hasSpoilerField)
val hasSpoiler: Boolean? = null,
@SerialName(captionField)
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(video, caption)
override val html: String = html(video, hasSpoiler, caption)
override val media: TelegramMediaFile
get() = video
companion object {
const val TYPE = "video"
fun markdown(video: VideoFile, caption: RichBlockCaption?): String =
richMediaMarkdown(video.fileId.fileId, caption)
fun html(video: VideoFile, hasSpoiler: Boolean?, caption: RichBlockCaption?): String =
richMediaHtml("video", video.fileId.fileId, hasSpoiler == true, selfClosing = false, caption = caption)
}
}
/**
* A block with a voice note.
*
* @see <a href="https://core.telegram.org/bots/api#richblockvoicenote">RichBlockVoiceNote</a>
*/
@Serializable
data class RichBlockVoiceNote(
@SerialName(voiceNoteField)
val voiceNote: VoiceFile,
@SerialName(captionField)
override val caption: RichBlockCaption? = null
) : RichBlockMedia {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(voiceNote, caption)
override val html: String = html(voiceNote, caption)
override val media: TelegramMediaFile
get() = voiceNote
companion object {
const val TYPE = "voice_note"
fun markdown(voiceNote: VoiceFile, caption: RichBlockCaption?): String =
richMediaMarkdown(voiceNote.fileId.fileId, caption)
fun html(voiceNote: VoiceFile, caption: RichBlockCaption?): String =
richMediaHtml("audio", voiceNote.fileId.fileId, spoiler = false, selfClosing = false, caption = caption)
}
}
/**
* A block with a "Thinking…" placeholder. May be used only in sendRichMessageDraft.
*
* @see <a href="https://core.telegram.org/bots/api#richblockthinking">RichBlockThinking</a>
*/
@Serializable
data class RichBlockThinking(
@SerialName(textField)
val text: RichText
) : RichBlock {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "thinking"
fun markdown(text: RichText): String = "<tg-thinking>${text.markdown}</tg-thinking>"
fun html(text: RichText): String = "<tg-thinking>${text.html}</tg-thinking>"
}
}

View File

@@ -0,0 +1,143 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.types.typeField
import dev.inmo.tgbotapi.utils.extensions.toHtml
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.*
/**
* Represents a rich formatted text. It can be either a plain text ([RichTextPlain]), a group of rich texts
* ([RichTextGroup]) or any of [RichTextEntity] subtypes.
*
* @see <a href="https://core.telegram.org/bots/api#richtext">RichText</a>
*/
@Serializable(RichTextSerializer::class)
@ClassCastsIncluded
sealed interface RichText {
/**
* Plain (unformatted) text of this [RichText]. For [RichTextEntity]s without an inner [RichText] it falls back to
* the most meaningful textual representation: alternative text for custom emojis, the expression for mathematical
* expressions and an empty string for anchors.
*/
val rawText: String
/**
* [Rich Markdown style](https://core.telegram.org/bots/api#rich-markdown-style) representation of this [RichText].
*/
val markdown: String
/**
* [Rich HTML style](https://core.telegram.org/bots/api#rich-html-style) representation of this [RichText].
*/
val html: String
}
/**
* A plain (non-formatted) part of a [RichText]. Serialized as a bare JSON string.
*/
@Serializable
data class RichTextPlain(
val text: String
) : RichText {
override val rawText: String = text
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
fun markdown(text: String): String = text.escapeRichMarkdown()
fun html(text: String): String = text.toHtml()
}
}
/**
* A group of [RichText]s. Serialized as a JSON array.
*/
@Serializable
data class RichTextGroup(
val parts: List<RichText>
) : RichText {
override val rawText: String = parts.joinToString(separator = "") { it.rawText }
override val markdown: String = markdown(parts)
override val html: String = html(parts)
companion object {
fun markdown(parts: List<RichText>): String = parts.joinToString(separator = "") { it.markdown }
fun html(parts: List<RichText>): String = parts.joinToString(separator = "") { it.html }
}
}
/**
* Any typed (formatted) part of a [RichText]. Serialized as a JSON object with the [type] discriminator.
*/
@Serializable(RichTextEntitySerializer::class)
sealed interface RichTextEntity : RichText {
val type: String
override val markdown: String
override val html: String
}
object RichTextSerializer : KSerializer<RichText> {
override val descriptor: SerialDescriptor = JsonElement.serializer().descriptor
private fun fromJson(json: Json, element: JsonElement): RichText = when (element) {
is JsonArray -> RichTextGroup(element.map { fromJson(json, it) })
is JsonObject -> json.decodeFromJsonElement(RichTextEntitySerializer, element)
is JsonPrimitive -> RichTextPlain(element.content)
}
private fun toJson(json: Json, value: RichText): JsonElement = when (value) {
is RichTextPlain -> JsonPrimitive(value.text)
is RichTextGroup -> JsonArray(value.parts.map { toJson(json, it) })
is RichTextEntity -> json.encodeToJsonElement(RichTextEntitySerializer, value)
}
override fun deserialize(decoder: Decoder): RichText {
val input = decoder as JsonDecoder
return fromJson(input.json, input.decodeJsonElement())
}
override fun serialize(encoder: Encoder, value: RichText) {
val output = encoder as JsonEncoder
output.encodeJsonElement(toJson(output.json, value))
}
}
object RichTextEntitySerializer : JsonContentPolymorphicSerializer<RichTextEntity>(RichTextEntity::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<RichTextEntity> {
return when (val type = element.jsonObject[typeField]?.jsonPrimitive?.content) {
RichTextBold.TYPE -> RichTextBold.serializer()
RichTextItalic.TYPE -> RichTextItalic.serializer()
RichTextUnderline.TYPE -> RichTextUnderline.serializer()
RichTextStrikethrough.TYPE -> RichTextStrikethrough.serializer()
RichTextSpoiler.TYPE -> RichTextSpoiler.serializer()
RichTextDateTime.TYPE -> RichTextDateTime.serializer()
RichTextTextMention.TYPE -> RichTextTextMention.serializer()
RichTextSubscript.TYPE -> RichTextSubscript.serializer()
RichTextSuperscript.TYPE -> RichTextSuperscript.serializer()
RichTextMarked.TYPE -> RichTextMarked.serializer()
RichTextCode.TYPE -> RichTextCode.serializer()
RichTextCustomEmoji.TYPE -> RichTextCustomEmoji.serializer()
RichTextMathematicalExpression.TYPE -> RichTextMathematicalExpression.serializer()
RichTextUrl.TYPE -> RichTextUrl.serializer()
RichTextEmailAddress.TYPE -> RichTextEmailAddress.serializer()
RichTextPhoneNumber.TYPE -> RichTextPhoneNumber.serializer()
RichTextBankCardNumber.TYPE -> RichTextBankCardNumber.serializer()
RichTextMention.TYPE -> RichTextMention.serializer()
RichTextHashtag.TYPE -> RichTextHashtag.serializer()
RichTextCashtag.TYPE -> RichTextCashtag.serializer()
RichTextBotCommand.TYPE -> RichTextBotCommand.serializer()
RichTextAnchor.TYPE -> RichTextAnchor.serializer()
RichTextAnchorLink.TYPE -> RichTextAnchorLink.serializer()
RichTextReference.TYPE -> RichTextReference.serializer()
RichTextReferenceLink.TYPE -> RichTextReferenceLink.serializer()
else -> error("Unknown RichTextEntity type: $type")
}
}
}

View File

@@ -0,0 +1,209 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.types.CustomEmojiId
import dev.inmo.tgbotapi.types.TelegramDate
import dev.inmo.tgbotapi.types.chat.User
/**
* [DslMarker] for the rich message builders, so the inner [RichTextBuilder], [RichBlocksBuilder] and
* [RichBlockListBuilder] scopes do not leak their receivers into each other.
*/
@DslMarker
annotation class RichTextDsl
/**
* Builder of a single [RichText]. Each call appends a part; [build] returns a [RichTextPlain]/[RichTextEntity] when there
* is exactly one part, a [RichTextGroup] otherwise.
*/
@RichTextDsl
class RichTextBuilder {
private val parts = mutableListOf<RichText>()
/** Appends an already built [RichText]. */
fun add(richText: RichText) {
parts.add(richText)
}
/** Appends an already built [RichText]. */
operator fun RichText.unaryPlus() = add(this)
/** Plain, non-formatted text. */
fun plain(text: String) = add(RichTextPlain(text))
fun bold(text: String) = add(RichTextBold(RichTextPlain(text)))
fun bold(block: RichTextBuilder.() -> Unit) = add(RichTextBold(buildRichText(block)))
fun italic(text: String) = add(RichTextItalic(RichTextPlain(text)))
fun italic(block: RichTextBuilder.() -> Unit) = add(RichTextItalic(buildRichText(block)))
fun underline(text: String) = add(RichTextUnderline(RichTextPlain(text)))
fun underline(block: RichTextBuilder.() -> Unit) = add(RichTextUnderline(buildRichText(block)))
fun strikethrough(text: String) = add(RichTextStrikethrough(RichTextPlain(text)))
fun strikethrough(block: RichTextBuilder.() -> Unit) = add(RichTextStrikethrough(buildRichText(block)))
fun spoiler(text: String) = add(RichTextSpoiler(RichTextPlain(text)))
fun spoiler(block: RichTextBuilder.() -> Unit) = add(RichTextSpoiler(buildRichText(block)))
fun subscript(text: String) = add(RichTextSubscript(RichTextPlain(text)))
fun subscript(block: RichTextBuilder.() -> Unit) = add(RichTextSubscript(buildRichText(block)))
fun superscript(text: String) = add(RichTextSuperscript(RichTextPlain(text)))
fun superscript(block: RichTextBuilder.() -> Unit) = add(RichTextSuperscript(buildRichText(block)))
fun marked(text: String) = add(RichTextMarked(RichTextPlain(text)))
fun marked(block: RichTextBuilder.() -> Unit) = add(RichTextMarked(buildRichText(block)))
fun code(text: String) = add(RichTextCode(RichTextPlain(text)))
fun code(block: RichTextBuilder.() -> Unit) = add(RichTextCode(buildRichText(block)))
fun dateTime(text: String, unixTime: TelegramDate, dateTimeFormat: String) =
add(RichTextDateTime(RichTextPlain(text), unixTime, dateTimeFormat))
fun dateTime(unixTime: TelegramDate, dateTimeFormat: String, block: RichTextBuilder.() -> Unit) =
add(RichTextDateTime(buildRichText(block), unixTime, dateTimeFormat))
fun textMention(text: String, user: User) = add(RichTextTextMention(RichTextPlain(text), user))
fun textMention(user: User, block: RichTextBuilder.() -> Unit) = add(RichTextTextMention(buildRichText(block), user))
fun customEmoji(customEmojiId: CustomEmojiId, alternativeText: String) =
add(RichTextCustomEmoji(customEmojiId, alternativeText))
fun mathematicalExpression(expression: String) = add(RichTextMathematicalExpression(expression))
fun url(text: String, url: String) = add(RichTextUrl(RichTextPlain(text), url))
fun url(url: String, block: RichTextBuilder.() -> Unit) = add(RichTextUrl(buildRichText(block), url))
fun email(text: String, emailAddress: String) = add(RichTextEmailAddress(RichTextPlain(text), emailAddress))
fun email(emailAddress: String, block: RichTextBuilder.() -> Unit) =
add(RichTextEmailAddress(buildRichText(block), emailAddress))
fun phone(text: String, phoneNumber: String) = add(RichTextPhoneNumber(RichTextPlain(text), phoneNumber))
fun phone(phoneNumber: String, block: RichTextBuilder.() -> Unit) =
add(RichTextPhoneNumber(buildRichText(block), phoneNumber))
fun bankCard(text: String, bankCardNumber: String) = add(RichTextBankCardNumber(RichTextPlain(text), bankCardNumber))
fun bankCard(bankCardNumber: String, block: RichTextBuilder.() -> Unit) =
add(RichTextBankCardNumber(buildRichText(block), bankCardNumber))
fun mention(text: String, username: String) = add(RichTextMention(RichTextPlain(text), username))
fun mention(username: String, block: RichTextBuilder.() -> Unit) = add(RichTextMention(buildRichText(block), username))
fun hashtag(text: String, hashtag: String) = add(RichTextHashtag(RichTextPlain(text), hashtag))
fun hashtag(hashtag: String, block: RichTextBuilder.() -> Unit) = add(RichTextHashtag(buildRichText(block), hashtag))
fun cashtag(text: String, cashtag: String) = add(RichTextCashtag(RichTextPlain(text), cashtag))
fun cashtag(cashtag: String, block: RichTextBuilder.() -> Unit) = add(RichTextCashtag(buildRichText(block), cashtag))
fun botCommand(text: String, botCommand: String) = add(RichTextBotCommand(RichTextPlain(text), botCommand))
fun botCommand(botCommand: String, block: RichTextBuilder.() -> Unit) =
add(RichTextBotCommand(buildRichText(block), botCommand))
fun anchor(name: String) = add(RichTextAnchor(name))
fun anchorLink(text: String, anchorName: String) = add(RichTextAnchorLink(RichTextPlain(text), anchorName))
fun anchorLink(anchorName: String, block: RichTextBuilder.() -> Unit) =
add(RichTextAnchorLink(buildRichText(block), anchorName))
fun reference(text: String, name: String) = add(RichTextReference(RichTextPlain(text), name))
fun reference(name: String, block: RichTextBuilder.() -> Unit) = add(RichTextReference(buildRichText(block), name))
fun referenceLink(text: String, referenceName: String) =
add(RichTextReferenceLink(RichTextPlain(text), referenceName))
fun referenceLink(referenceName: String, block: RichTextBuilder.() -> Unit) =
add(RichTextReferenceLink(buildRichText(block), referenceName))
fun build(): RichText = when (parts.size) {
0 -> RichTextGroup(emptyList())
1 -> parts.single()
else -> RichTextGroup(parts.toList())
}
}
/**
* Builder of [RichBlockListItem]s used inside [RichBlocksBuilder.list].
*/
@RichTextDsl
class RichBlockListBuilder {
private val items = mutableListOf<RichBlockListItem>()
fun item(
label: String,
hasCheckbox: Boolean? = null,
isChecked: Boolean? = null,
value: Int? = null,
labelType: String? = null,
block: RichBlocksBuilder.() -> Unit
) {
items.add(RichBlockListItem(label, buildRichBlocks(block), hasCheckbox, isChecked, value, labelType))
}
fun item(label: String, text: String) {
items.add(RichBlockListItem(label, listOf(RichBlockParagraph(RichTextPlain(text)))))
}
fun build(): List<RichBlockListItem> = items.toList()
}
/**
* Builder of a [List] of [RichBlock]s - the root of the rich message DSL. Text-bearing and container blocks have their
* own DSL functions; file/cell-heavy blocks (media, collage, slideshow, table, map) can be appended with [add] / unary
* plus.
*/
@RichTextDsl
class RichBlocksBuilder {
private val blocks = mutableListOf<RichBlock>()
/** Appends an already built [RichBlock]. */
fun add(block: RichBlock) {
blocks.add(block)
}
/** Appends an already built [RichBlock]. */
operator fun RichBlock.unaryPlus() = add(this)
fun paragraph(text: String) = add(RichBlockParagraph(RichTextPlain(text)))
fun paragraph(block: RichTextBuilder.() -> Unit) = add(RichBlockParagraph(buildRichText(block)))
fun heading(text: String, level: Int) = add(RichBlockSectionHeading(RichTextPlain(text), level))
fun heading(level: Int, block: RichTextBuilder.() -> Unit) = add(RichBlockSectionHeading(buildRichText(block), level))
fun preformatted(text: String, language: String? = null) = add(RichBlockPreformatted(RichTextPlain(text), language))
fun footer(text: String) = add(RichBlockFooter(RichTextPlain(text)))
fun footer(block: RichTextBuilder.() -> Unit) = add(RichBlockFooter(buildRichText(block)))
fun divider() = add(RichBlockDivider())
fun mathematicalExpression(expression: String) = add(RichBlockMathematicalExpression(expression))
fun anchor(name: String) = add(RichBlockAnchor(name))
fun thinking(text: String) = add(RichBlockThinking(RichTextPlain(text)))
fun thinking(block: RichTextBuilder.() -> Unit) = add(RichBlockThinking(buildRichText(block)))
fun list(block: RichBlockListBuilder.() -> Unit) = add(RichBlockList(RichBlockListBuilder().apply(block).build()))
fun blockQuotation(credit: RichText? = null, block: RichBlocksBuilder.() -> Unit) =
add(RichBlockBlockQuotation(buildRichBlocks(block), credit))
fun pullQuotation(credit: RichText? = null, block: RichTextBuilder.() -> Unit) =
add(RichBlockPullQuotation(buildRichText(block), credit))
fun details(summary: RichText, isOpen: Boolean? = null, block: RichBlocksBuilder.() -> Unit) =
add(RichBlockDetails(summary, buildRichBlocks(block), isOpen))
fun details(summary: String, isOpen: Boolean? = null, block: RichBlocksBuilder.() -> Unit) =
details(RichTextPlain(summary), isOpen, block)
fun build(): List<RichBlock> = blocks.toList()
}
/** Builds a [RichText] using the [RichTextBuilder] DSL. */
fun buildRichText(block: RichTextBuilder.() -> Unit): RichText = RichTextBuilder().apply(block).build()
/** Builds a [List] of [RichBlock]s using the [RichBlocksBuilder] DSL. */
fun buildRichBlocks(block: RichBlocksBuilder.() -> Unit): List<RichBlock> = RichBlocksBuilder().apply(block).build()
/** Builds a [RichTextInfo] using the [RichBlocksBuilder] DSL. */
fun buildRichTextInfo(isRtl: Boolean? = null, block: RichBlocksBuilder.() -> Unit): RichTextInfo =
RichTextInfo(buildRichBlocks(block), isRtl)

View File

@@ -0,0 +1,690 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.types.CustomEmojiId
import dev.inmo.tgbotapi.types.alternativeTextField
import dev.inmo.tgbotapi.types.anchorNameField
import dev.inmo.tgbotapi.types.bankCardNumberField
import dev.inmo.tgbotapi.types.botCommandFullField
import dev.inmo.tgbotapi.types.cashtagField
import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.customEmojiIdField
import dev.inmo.tgbotapi.types.dateTimeFormatField
import dev.inmo.tgbotapi.types.emailAddressField
import dev.inmo.tgbotapi.types.expressionField
import dev.inmo.tgbotapi.types.hashtagField
import dev.inmo.tgbotapi.types.internalUserLinkBeginning
import dev.inmo.tgbotapi.types.nameField
import dev.inmo.tgbotapi.types.phoneNumberField
import dev.inmo.tgbotapi.types.referenceNameField
import dev.inmo.tgbotapi.types.TelegramDate
import dev.inmo.tgbotapi.types.textField
import dev.inmo.tgbotapi.types.typeField
import dev.inmo.tgbotapi.types.unixTimeField
import dev.inmo.tgbotapi.types.urlField
import dev.inmo.tgbotapi.types.userField
import dev.inmo.tgbotapi.types.usernameField
import dev.inmo.tgbotapi.utils.extensions.toHtml
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* A bold [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextbold">RichTextBold</a>
*/
@Serializable
data class RichTextBold(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "bold"
fun markdown(text: RichText): String = "**${text.markdown}**"
fun html(text: RichText): String = "<b>${text.html}</b>"
}
}
/**
* An italicized [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextitalic">RichTextItalic</a>
*/
@Serializable
data class RichTextItalic(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "italic"
fun markdown(text: RichText): String = "*${text.markdown}*"
fun html(text: RichText): String = "<i>${text.html}</i>"
}
}
/**
* An underlined [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextunderline">RichTextUnderline</a>
*/
@Serializable
data class RichTextUnderline(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "underline"
fun markdown(text: RichText): String = "<u>${text.markdown}</u>"
fun html(text: RichText): String = "<u>${text.html}</u>"
}
}
/**
* A strikethrough [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextstrikethrough">RichTextStrikethrough</a>
*/
@Serializable
data class RichTextStrikethrough(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "strikethrough"
fun markdown(text: RichText): String = "~~${text.markdown}~~"
fun html(text: RichText): String = "<s>${text.html}</s>"
}
}
/**
* A [RichTextEntity] covered by a spoiler.
*
* @see <a href="https://core.telegram.org/bots/api#richtextspoiler">RichTextSpoiler</a>
*/
@Serializable
data class RichTextSpoiler(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "spoiler"
fun markdown(text: RichText): String = "||${text.markdown}||"
fun html(text: RichText): String = "<tg-spoiler>${text.html}</tg-spoiler>"
}
}
/**
* A subscript [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextsubscript">RichTextSubscript</a>
*/
@Serializable
data class RichTextSubscript(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "subscript"
fun markdown(text: RichText): String = "<sub>${text.markdown}</sub>"
fun html(text: RichText): String = "<sub>${text.html}</sub>"
}
}
/**
* A superscript [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextsuperscript">RichTextSuperscript</a>
*/
@Serializable
data class RichTextSuperscript(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "superscript"
fun markdown(text: RichText): String = "<sup>${text.markdown}</sup>"
fun html(text: RichText): String = "<sup>${text.html}</sup>"
}
}
/**
* A marked [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextmarked">RichTextMarked</a>
*/
@Serializable
data class RichTextMarked(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "marked"
fun markdown(text: RichText): String = "==${text.markdown}=="
fun html(text: RichText): String = "<mark>${text.html}</mark>"
}
}
/**
* A monowidth [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextcode">RichTextCode</a>
*/
@Serializable
data class RichTextCode(
@SerialName(textField)
val text: RichText
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "code"
fun markdown(text: RichText): String = "`${text.rawText}`"
fun html(text: RichText): String = "<code>${text.html}</code>"
}
}
/**
* A formatted date and time [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextdatetime">RichTextDateTime</a>
*/
@Serializable
data class RichTextDateTime(
@SerialName(textField)
val text: RichText,
@SerialName(unixTimeField)
val unixTime: TelegramDate,
@SerialName(dateTimeFormatField)
val dateTimeFormat: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text, unixTime, dateTimeFormat)
override val html: String = html(text, unixTime, dateTimeFormat)
companion object {
const val TYPE = "date_time"
fun markdown(text: RichText, unixTime: TelegramDate, dateTimeFormat: String): String =
"![${text.markdown}](tg://time?unix=${unixTime.date}&format=$dateTimeFormat)"
fun html(text: RichText, unixTime: TelegramDate, dateTimeFormat: String): String =
"<tg-time unix=\"${unixTime.date}\" format=\"$dateTimeFormat\">${text.html}</tg-time>"
}
}
/**
* A mention of a Telegram user by their identifier.
*
* @see <a href="https://core.telegram.org/bots/api#richtexttextmention">RichTextTextMention</a>
*/
@Serializable
data class RichTextTextMention(
@SerialName(textField)
val text: RichText,
@SerialName(userField)
val user: User
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text, user)
override val html: String = html(text, user)
companion object {
const val TYPE = "text_mention"
fun markdown(text: RichText, user: User): String =
"[${text.markdown}]($internalUserLinkBeginning${user.id.chatId.long})"
fun html(text: RichText, user: User): String =
"<a href=\"$internalUserLinkBeginning${user.id.chatId.long}\">${text.html}</a>"
}
}
/**
* A custom emoji [RichTextEntity].
*
* @see <a href="https://core.telegram.org/bots/api#richtextcustomemoji">RichTextCustomEmoji</a>
*/
@Serializable
data class RichTextCustomEmoji(
@SerialName(customEmojiIdField)
val customEmojiId: CustomEmojiId,
@SerialName(alternativeTextField)
val alternativeText: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = alternativeText
override val markdown: String = markdown(customEmojiId, alternativeText)
override val html: String = html(customEmojiId, alternativeText)
companion object {
const val TYPE = "custom_emoji"
fun markdown(customEmojiId: CustomEmojiId, alternativeText: String): String =
"![${alternativeText.escapeRichMarkdown()}](tg://emoji?id=${customEmojiId.string})"
fun html(customEmojiId: CustomEmojiId, alternativeText: String): String =
"<tg-emoji emoji-id=\"${customEmojiId.string}\">${alternativeText.toHtml()}</tg-emoji>"
}
}
/**
* A mathematical expression in LaTeX format.
*
* @see <a href="https://core.telegram.org/bots/api#richtextmathematicalexpression">RichTextMathematicalExpression</a>
*/
@Serializable
data class RichTextMathematicalExpression(
@SerialName(expressionField)
val expression: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = expression
override val markdown: String = markdown(expression)
override val html: String = html(expression)
companion object {
const val TYPE = "mathematical_expression"
fun markdown(expression: String): String = "\$$expression\$"
fun html(expression: String): String = "<tg-math>$expression</tg-math>"
}
}
/**
* A [RichTextEntity] with a link.
*
* @see <a href="https://core.telegram.org/bots/api#richtexturl">RichTextUrl</a>
*/
@Serializable
data class RichTextUrl(
@SerialName(textField)
val text: RichText,
@SerialName(urlField)
val url: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text, url)
override val html: String = html(text, url)
companion object {
const val TYPE = "url"
fun markdown(text: RichText, url: String): String = "[${text.markdown}]($url)"
fun html(text: RichText, url: String): String = "<a href=\"$url\">${text.html}</a>"
}
}
/**
* A [RichTextEntity] with an email address.
*
* @see <a href="https://core.telegram.org/bots/api#richtextemailaddress">RichTextEmailAddress</a>
*/
@Serializable
data class RichTextEmailAddress(
@SerialName(textField)
val text: RichText,
@SerialName(emailAddressField)
val emailAddress: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text, emailAddress)
override val html: String = html(text, emailAddress)
companion object {
const val TYPE = "email_address"
fun markdown(text: RichText, emailAddress: String): String = "[${text.markdown}](mailto:$emailAddress)"
fun html(text: RichText, emailAddress: String): String = "<a href=\"mailto:$emailAddress\">${text.html}</a>"
}
}
/**
* A [RichTextEntity] with a phone number.
*
* @see <a href="https://core.telegram.org/bots/api#richtextphonenumber">RichTextPhoneNumber</a>
*/
@Serializable
data class RichTextPhoneNumber(
@SerialName(textField)
val text: RichText,
@SerialName(phoneNumberField)
val phoneNumber: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text, phoneNumber)
override val html: String = html(text, phoneNumber)
companion object {
const val TYPE = "phone_number"
fun markdown(text: RichText, phoneNumber: String): String = "[${text.markdown}](tel:$phoneNumber)"
fun html(text: RichText, phoneNumber: String): String = "<a href=\"tel:$phoneNumber\">${text.html}</a>"
}
}
/**
* A [RichTextEntity] with a bank card number.
*
* @see <a href="https://core.telegram.org/bots/api#richtextbankcardnumber">RichTextBankCardNumber</a>
*/
@Serializable
data class RichTextBankCardNumber(
@SerialName(textField)
val text: RichText,
@SerialName(bankCardNumberField)
val bankCardNumber: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "bank_card_number"
fun markdown(text: RichText): String = text.markdown
fun html(text: RichText): String = text.html
}
}
/**
* A mention by a username.
*
* @see <a href="https://core.telegram.org/bots/api#richtextmention">RichTextMention</a>
*/
@Serializable
data class RichTextMention(
@SerialName(textField)
val text: RichText,
@SerialName(usernameField)
val username: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "mention"
fun markdown(text: RichText): String = text.markdown
fun html(text: RichText): String = text.html
}
}
/**
* A hashtag.
*
* @see <a href="https://core.telegram.org/bots/api#richtexthashtag">RichTextHashtag</a>
*/
@Serializable
data class RichTextHashtag(
@SerialName(textField)
val text: RichText,
@SerialName(hashtagField)
val hashtag: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "hashtag"
fun markdown(text: RichText): String = text.markdown
fun html(text: RichText): String = text.html
}
}
/**
* A cashtag.
*
* @see <a href="https://core.telegram.org/bots/api#richtextcashtag">RichTextCashtag</a>
*/
@Serializable
data class RichTextCashtag(
@SerialName(textField)
val text: RichText,
@SerialName(cashtagField)
val cashtag: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "cashtag"
fun markdown(text: RichText): String = text.markdown
fun html(text: RichText): String = text.html
}
}
/**
* A bot command.
*
* @see <a href="https://core.telegram.org/bots/api#richtextbotcommand">RichTextBotCommand</a>
*/
@Serializable
data class RichTextBotCommand(
@SerialName(textField)
val text: RichText,
@SerialName(botCommandFullField)
val botCommand: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text)
override val html: String = html(text)
companion object {
const val TYPE = "bot_command"
fun markdown(text: RichText): String = text.markdown
fun html(text: RichText): String = text.html
}
}
/**
* An anchor.
*
* @see <a href="https://core.telegram.org/bots/api#richtextanchor">RichTextAnchor</a>
*/
@Serializable
data class RichTextAnchor(
@SerialName(nameField)
val name: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = ""
override val markdown: String = markdown(name)
override val html: String = html(name)
companion object {
const val TYPE = "anchor"
fun markdown(name: String): String = "<a name=\"$name\"></a>"
fun html(name: String): String = "<a name=\"$name\"></a>"
}
}
/**
* A link to an anchor.
*
* @see <a href="https://core.telegram.org/bots/api#richtextanchorlink">RichTextAnchorLink</a>
*/
@Serializable
data class RichTextAnchorLink(
@SerialName(textField)
val text: RichText,
@SerialName(anchorNameField)
val anchorName: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text, anchorName)
override val html: String = html(text, anchorName)
companion object {
const val TYPE = "anchor_link"
fun markdown(text: RichText, anchorName: String): String = "[${text.markdown}](#$anchorName)"
fun html(text: RichText, anchorName: String): String = "<a href=\"#$anchorName\">${text.html}</a>"
}
}
/**
* A reference.
*
* @see <a href="https://core.telegram.org/bots/api#richtextreference">RichTextReference</a>
*/
@Serializable
data class RichTextReference(
@SerialName(textField)
val text: RichText,
@SerialName(nameField)
val name: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text, name)
override val html: String = html(text, name)
companion object {
const val TYPE = "reference"
fun markdown(text: RichText, name: String): String = "<tg-reference name=\"$name\">${text.markdown}</tg-reference>"
fun html(text: RichText, name: String): String = "<tg-reference name=\"$name\">${text.html}</tg-reference>"
}
}
/**
* A link to a reference.
*
* @see <a href="https://core.telegram.org/bots/api#richtextreferencelink">RichTextReferenceLink</a>
*/
@Serializable
data class RichTextReferenceLink(
@SerialName(textField)
val text: RichText,
@SerialName(referenceNameField)
val referenceName: String
) : RichTextEntity {
@EncodeDefault
@SerialName(typeField)
override val type: String = TYPE
override val rawText: String = text.rawText
override val markdown: String = markdown(text, referenceName)
override val html: String = html(text, referenceName)
companion object {
const val TYPE = "reference_link"
fun markdown(text: RichText, referenceName: String): String = "[${text.markdown}](#$referenceName)"
fun html(text: RichText, referenceName: String): String = "<a href=\"#$referenceName\">${text.html}</a>"
}
}

View File

@@ -0,0 +1,23 @@
package dev.inmo.tgbotapi.types.rich
/**
* Characters which have a special meaning in the
* [Rich Markdown style](https://core.telegram.org/bots/api#rich-markdown-style) and must be escaped with a backslash
* to be represented literally.
*/
private val richMarkdownSpecialCharacters = setOf(
'\\', '`', '*', '_', '~', '|', '[', ']', '(', ')', '<', '>', '#', '=', '!', '$'
)
/**
* Escapes all the [richMarkdownSpecialCharacters] of the receiver with a backslash so that the resulting string is
* represented literally in the [Rich Markdown style](https://core.telegram.org/bots/api#rich-markdown-style).
*/
fun String.escapeRichMarkdown(): String = buildString {
for (character in this@escapeRichMarkdown) {
if (character in richMarkdownSpecialCharacters) {
append('\\')
}
append(character)
}
}

View File

@@ -0,0 +1,19 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.types.blocksField
import dev.inmo.tgbotapi.types.isRtlField
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Rich formatted message.
*
* @see <a href="https://core.telegram.org/bots/api#richmessage">RichMessage</a>
*/
@Serializable
data class RichTextInfo(
@SerialName(blocksField)
val blocks: List<RichBlock>,
@SerialName(isRtlField)
val isRtl: Boolean? = null
)

View File

@@ -0,0 +1,71 @@
package dev.inmo.tgbotapi.types
import dev.inmo.tgbotapi.types.rich.*
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class RichMessageSerializationTest {
private val json = Json { encodeDefaults = true }
@Test
fun decodesRichMessageWithMixedRichText() {
val source = """
{
"blocks": [
{
"type": "paragraph",
"text": ["Hello ", {"type": "bold", "text": "world"}, "!"]
},
{"type": "heading", "text": "Title", "size": 1},
{"type": "divider"},
{"type": "list", "items": [
{"label": "1", "blocks": [{"type": "paragraph", "text": "first"}]}
]}
],
"is_rtl": false
}
""".trimIndent()
val message = json.decodeFromString(RichTextInfo.serializer(), source)
assertEquals(4, message.blocks.size)
val paragraph = message.blocks[0] as RichBlockParagraph
val group = paragraph.text as RichTextGroup
assertEquals(RichTextPlain("Hello "), group.parts[0])
assertEquals(RichTextBold(RichTextPlain("world")), group.parts[1])
assertEquals(RichTextPlain("!"), group.parts[2])
val heading = message.blocks[1] as RichBlockSectionHeading
assertEquals(RichTextPlain("Title"), heading.text)
assertEquals(1, heading.level)
assertTrue(message.blocks[2] is RichBlockDivider)
val list = message.blocks[3] as RichBlockList
assertEquals("1", list.items[0].label)
}
@Test
fun roundTripsRichMessage() {
val message = RichTextInfo(
blocks = listOf(
RichBlockParagraph(
RichTextGroup(
listOf(
RichTextPlain("a "),
RichTextItalic(RichTextPlain("b")),
RichTextUrl(RichTextPlain("link"), "https://example.org")
)
)
),
RichBlockDivider()
)
)
val encoded = json.encodeToString(RichTextInfo.serializer(), message)
val decoded = json.decodeFromString(RichTextInfo.serializer(), encoded)
assertEquals(message, decoded)
}
}

View File

@@ -0,0 +1,141 @@
package dev.inmo.tgbotapi.types.rich
import kotlin.test.Test
import kotlin.test.assertEquals
class RichBlockFormattingTest {
@Test
fun paragraph() {
val block = RichBlockParagraph(RichTextPlain("Hello"))
assertEquals("Hello", block.markdown)
assertEquals("<p>Hello</p>", block.html)
}
@Test
fun heading() {
val block = RichBlockSectionHeading(RichTextPlain("Title"), 2)
assertEquals("## Title", block.markdown)
assertEquals("<h2>Title</h2>", block.html)
}
@Test
fun divider() {
val block = RichBlockDivider()
assertEquals("---", block.markdown)
assertEquals("<hr/>", block.html)
}
@Test
fun footer() {
val block = RichBlockFooter(RichTextPlain("f"))
assertEquals("<footer>f</footer>", block.markdown)
assertEquals("<footer>f</footer>", block.html)
}
@Test
fun preformatted() {
val withLanguage = RichBlockPreformatted(RichTextPlain("code"), "kotlin")
assertEquals("```kotlin\ncode\n```", withLanguage.markdown)
assertEquals("<pre><code class=\"language-kotlin\">code</code></pre>", withLanguage.html)
val withoutLanguage = RichBlockPreformatted(RichTextPlain("c"))
assertEquals("```\nc\n```", withoutLanguage.markdown)
assertEquals("<pre>c</pre>", withoutLanguage.html)
}
@Test
fun mathematicalExpression() {
val block = RichBlockMathematicalExpression("E=mc^2")
assertEquals("\$\$E=mc^2\$\$", block.markdown)
assertEquals("<tg-math-block>E=mc^2</tg-math-block>", block.html)
}
@Test
fun anchor() {
val block = RichBlockAnchor("top")
assertEquals("<a name=\"top\"></a>", block.markdown)
assertEquals("<a name=\"top\"></a>", block.html)
}
@Test
fun bulletList() {
val block = RichBlockList(listOf(RichBlockListItem("-", listOf(RichBlockParagraph(RichTextPlain("one"))))))
assertEquals("- one", block.markdown)
assertEquals("<ul><li><p>one</p></li></ul>", block.html)
}
@Test
fun orderedList() {
val block = RichBlockList(
listOf(RichBlockListItem("1", listOf(RichBlockParagraph(RichTextPlain("one"))), labelType = "1"))
)
assertEquals("1. one", block.markdown)
assertEquals("<ol><li type=\"1\"><p>one</p></li></ol>", block.html)
}
@Test
fun taskList() {
val block = RichBlockList(
listOf(
RichBlockListItem(
"x",
listOf(RichBlockParagraph(RichTextPlain("done"))),
hasCheckbox = true,
isChecked = true
)
)
)
assertEquals("- [x] done", block.markdown)
assertEquals("<ul><li><input type=\"checkbox\" checked><p>done</p></li></ul>", block.html)
}
@Test
fun blockQuotation() {
val block = RichBlockBlockQuotation(listOf(RichBlockParagraph(RichTextPlain("q"))))
assertEquals("> q", block.markdown)
assertEquals("<blockquote><p>q</p></blockquote>", block.html)
}
@Test
fun details() {
val block = RichBlockDetails(RichTextPlain("sum"), listOf(RichBlockParagraph(RichTextPlain("body"))), isOpen = true)
assertEquals("<details open><summary>sum</summary>\n\nbody\n\n</details>", block.markdown)
assertEquals("<details open><summary>sum</summary><p>body</p></details>", block.html)
}
@Test
fun table() {
val block = RichBlockTable(
listOf(
listOf(
RichBlockTableCell(text = RichTextPlain("H1"), isHeader = true, align = "left", valign = "top"),
RichBlockTableCell(text = RichTextPlain("H2"), isHeader = true, align = "center", valign = "top")
),
listOf(
RichBlockTableCell(text = RichTextPlain("a"), align = "left", valign = "top"),
RichBlockTableCell(text = RichTextPlain("b"), align = "center", valign = "top")
)
)
)
assertEquals("| H1 | H2 |\n| :--- | :--: |\n| a | b |", block.markdown)
assertEquals(
"<table><tr><th align=\"left\" valign=\"top\">H1</th><th align=\"center\" valign=\"top\">H2</th></tr>" +
"<tr><td align=\"left\" valign=\"top\">a</td><td align=\"center\" valign=\"top\">b</td></tr></table>",
block.html
)
}
@Test
fun listOfBlocksJoinsBlocks() {
val blocks = listOf(RichBlockParagraph(RichTextPlain("a")), RichBlockDivider())
assertEquals("a\n\n---", blocks.toRichMarkdown())
assertEquals("<p>a</p>\n<hr/>", blocks.toRichHtml())
}
@Test
fun richTextInfoDelegatesToBlocks() {
val info = RichTextInfo(listOf(RichBlockParagraph(RichTextPlain("p")), RichBlockDivider()))
assertEquals("p\n\n---", info.markdown)
assertEquals("<p>p</p>\n<hr/>", info.html)
}
}

View File

@@ -0,0 +1,77 @@
package dev.inmo.tgbotapi.types.rich
import kotlin.test.Test
import kotlin.test.assertEquals
class RichTextDslTest {
@Test
fun buildsRichTextGroup() {
val richText = buildRichText {
plain("a ")
bold("b")
italic {
plain("c")
bold("d")
}
}
assertEquals(
RichTextGroup(
listOf(
RichTextPlain("a "),
RichTextBold(RichTextPlain("b")),
RichTextItalic(RichTextGroup(listOf(RichTextPlain("c"), RichTextBold(RichTextPlain("d")))))
)
),
richText
)
}
@Test
fun singlePartUnwraps() {
assertEquals(RichTextBold(RichTextPlain("x")), buildRichText { bold("x") })
}
@Test
fun rendersMarkdown() {
assertEquals("a **b**", buildRichText { plain("a "); bold("b") }.markdown)
}
@Test
fun buildsBlocks() {
val blocks = buildRichBlocks {
heading("Title", 1)
paragraph {
plain("Hello ")
bold("world")
}
divider()
list {
item("1", "first")
item("2", labelType = "1") { paragraph("second") }
}
blockQuotation {
paragraph("quoted")
}
}
assertEquals(5, blocks.size)
assertEquals(RichBlockSectionHeading(RichTextPlain("Title"), 1), blocks[0])
assertEquals(
RichBlockParagraph(RichTextGroup(listOf(RichTextPlain("Hello "), RichTextBold(RichTextPlain("world"))))),
blocks[1]
)
assertEquals(RichBlockDivider(), blocks[2])
val list = blocks[3] as RichBlockList
assertEquals(2, list.items.size)
assertEquals(RichBlockListItem("1", listOf(RichBlockParagraph(RichTextPlain("first")))), list.items[0])
assertEquals(RichBlockBlockQuotation(listOf(RichBlockParagraph(RichTextPlain("quoted")))), blocks[4])
}
@Test
fun buildsRichTextInfo() {
val info = buildRichTextInfo(isRtl = true) {
paragraph("p")
}
assertEquals(RichTextInfo(listOf(RichBlockParagraph(RichTextPlain("p"))), true), info)
assertEquals("p", info.markdown)
}
}

View File

@@ -0,0 +1,222 @@
package dev.inmo.tgbotapi.types.rich
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.CustomEmojiId
import dev.inmo.tgbotapi.types.RawChatId
import dev.inmo.tgbotapi.types.TelegramDate
import dev.inmo.tgbotapi.types.chat.CommonUser
import kotlin.test.Test
import kotlin.test.assertEquals
class RichTextFormattingTest {
@Test
fun bold() {
val entity = RichTextBold(RichTextPlain("x"))
assertEquals("**x**", entity.markdown)
assertEquals("<b>x</b>", entity.html)
}
@Test
fun italic() {
val entity = RichTextItalic(RichTextPlain("x"))
assertEquals("*x*", entity.markdown)
assertEquals("<i>x</i>", entity.html)
}
@Test
fun underline() {
val entity = RichTextUnderline(RichTextPlain("x"))
assertEquals("<u>x</u>", entity.markdown)
assertEquals("<u>x</u>", entity.html)
}
@Test
fun strikethrough() {
val entity = RichTextStrikethrough(RichTextPlain("x"))
assertEquals("~~x~~", entity.markdown)
assertEquals("<s>x</s>", entity.html)
}
@Test
fun spoiler() {
val entity = RichTextSpoiler(RichTextPlain("x"))
assertEquals("||x||", entity.markdown)
assertEquals("<tg-spoiler>x</tg-spoiler>", entity.html)
}
@Test
fun subscript() {
val entity = RichTextSubscript(RichTextPlain("x"))
assertEquals("<sub>x</sub>", entity.markdown)
assertEquals("<sub>x</sub>", entity.html)
}
@Test
fun superscript() {
val entity = RichTextSuperscript(RichTextPlain("x"))
assertEquals("<sup>x</sup>", entity.markdown)
assertEquals("<sup>x</sup>", entity.html)
}
@Test
fun marked() {
val entity = RichTextMarked(RichTextPlain("x"))
assertEquals("==x==", entity.markdown)
assertEquals("<mark>x</mark>", entity.html)
}
@Test
fun code() {
val entity = RichTextCode(RichTextPlain("x"))
assertEquals("`x`", entity.markdown)
assertEquals("<code>x</code>", entity.html)
}
@Test
fun dateTime() {
val entity = RichTextDateTime(RichTextPlain("now"), TelegramDate(1647531900L), "wDT")
assertEquals("![now](tg://time?unix=1647531900&format=wDT)", entity.markdown)
assertEquals("<tg-time unix=\"1647531900\" format=\"wDT\">now</tg-time>", entity.html)
}
@Test
fun textMention() {
val entity = RichTextTextMention(RichTextPlain("John"), CommonUser(ChatId(RawChatId(12345L)), "John"))
assertEquals("[John](tg://user?id=12345)", entity.markdown)
assertEquals("<a href=\"tg://user?id=12345\">John</a>", entity.html)
}
@Test
fun customEmoji() {
val entity = RichTextCustomEmoji(CustomEmojiId("555"), "alt")
assertEquals("![alt](tg://emoji?id=555)", entity.markdown)
assertEquals("<tg-emoji emoji-id=\"555\">alt</tg-emoji>", entity.html)
}
@Test
fun mathematicalExpression() {
val entity = RichTextMathematicalExpression("x^2")
assertEquals("\$x^2\$", entity.markdown)
assertEquals("<tg-math>x^2</tg-math>", entity.html)
}
@Test
fun url() {
val entity = RichTextUrl(RichTextPlain("link"), "https://t.me/")
assertEquals("[link](https://t.me/)", entity.markdown)
assertEquals("<a href=\"https://t.me/\">link</a>", entity.html)
}
@Test
fun emailAddress() {
val entity = RichTextEmailAddress(RichTextPlain("mail"), "a@b.com")
assertEquals("[mail](mailto:a@b.com)", entity.markdown)
assertEquals("<a href=\"mailto:a@b.com\">mail</a>", entity.html)
}
@Test
fun phoneNumber() {
val entity = RichTextPhoneNumber(RichTextPlain("call"), "+123")
assertEquals("[call](tel:+123)", entity.markdown)
assertEquals("<a href=\"tel:+123\">call</a>", entity.html)
}
@Test
fun bankCardNumber() {
val entity = RichTextBankCardNumber(RichTextPlain("4242 4242 4242 4242"), "4242424242424242")
assertEquals("4242 4242 4242 4242", entity.markdown)
assertEquals("4242 4242 4242 4242", entity.html)
}
@Test
fun mention() {
val entity = RichTextMention(RichTextPlain("@user"), "user")
assertEquals("@user", entity.markdown)
assertEquals("@user", entity.html)
}
@Test
fun hashtag() {
val entity = RichTextHashtag(RichTextPlain("#tag"), "tag")
assertEquals("\\#tag", entity.markdown)
assertEquals("#tag", entity.html)
}
@Test
fun cashtag() {
val entity = RichTextCashtag(RichTextPlain("\$USD"), "USD")
assertEquals("\\\$USD", entity.markdown)
assertEquals("\$USD", entity.html)
}
@Test
fun botCommand() {
val entity = RichTextBotCommand(RichTextPlain("/start"), "start")
assertEquals("/start", entity.markdown)
assertEquals("/start", entity.html)
}
@Test
fun anchor() {
val entity = RichTextAnchor("top")
assertEquals("<a name=\"top\"></a>", entity.markdown)
assertEquals("<a name=\"top\"></a>", entity.html)
}
@Test
fun anchorLink() {
val entity = RichTextAnchorLink(RichTextPlain("go"), "top")
assertEquals("[go](#top)", entity.markdown)
assertEquals("<a href=\"#top\">go</a>", entity.html)
}
@Test
fun reference() {
val entity = RichTextReference(RichTextPlain("ref"), "note1")
assertEquals("<tg-reference name=\"note1\">ref</tg-reference>", entity.markdown)
assertEquals("<tg-reference name=\"note1\">ref</tg-reference>", entity.html)
}
@Test
fun referenceLink() {
val entity = RichTextReferenceLink(RichTextPlain("see"), "note1")
assertEquals("[see](#note1)", entity.markdown)
assertEquals("<a href=\"#note1\">see</a>", entity.html)
}
@Test
fun nestedGroupRecursesIntoInnerEntities() {
val entity = RichTextBold(
RichTextGroup(
listOf(
RichTextPlain("a "),
RichTextItalic(RichTextPlain("b"))
)
)
)
assertEquals("**a *b***", entity.markdown)
assertEquals("<b>a <i>b</i></b>", entity.html)
}
@Test
fun plainMarkdownEscapesSpecialCharacters() {
assertEquals("a\\*b\\_c", RichTextPlain("a*b_c").markdown)
assertEquals("\\[x\\]", RichTextPlain("[x]").markdown)
}
@Test
fun plainHtmlEscapesAngleBrackets() {
assertEquals("a&amp;lt;b", RichTextPlain("a<b").html)
}
@Test
fun rawTextExtractsPlainText() {
assertEquals("x", RichTextBold(RichTextPlain("x")).rawText)
assertEquals("alt", RichTextCustomEmoji(CustomEmojiId("1"), "alt").rawText)
assertEquals("e", RichTextMathematicalExpression("e").rawText)
assertEquals(
"a b",
RichTextGroup(listOf(RichTextPlain("a "), RichTextBold(RichTextPlain("b")))).rawText
)
}
}

View File

@@ -1906,6 +1906,7 @@ public final class dev/inmo/tgbotapi/extensions/utils/ClassCastsNewKt {
public static final fun ifInputInvoiceMessageContent (Ldev/inmo/tgbotapi/abstracts/CommonSendInvoiceData;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifInputInvoiceMessageContent (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifInputLocationMessageContent (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifInputRichMessageContent (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifInputTextMessageContent (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifInputVenueMessageContent (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifInternal (Ldev/inmo/tgbotapi/types/ReplyInfo;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
@@ -1918,6 +1919,7 @@ public final class dev/inmo/tgbotapi/extensions/utils/ClassCastsNewKt {
public static final fun ifLeftChatMember (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifLeftChatMemberEvent (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifLeftChatMemberEvent (Ldev/inmo/tgbotapi/types/message/ChatEvents/abstracts/ChatEvent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifLink (Ldev/inmo/tgbotapi/types/media/BaseTelegramMediaFile;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifLiveLocation (Ldev/inmo/tgbotapi/types/location/Location;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifLiveLocationContent (Ldev/inmo/tgbotapi/types/message/content/ResendableContent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifLivePhotoContent (Ldev/inmo/tgbotapi/types/message/content/ResendableContent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
@@ -2081,6 +2083,57 @@ public final class dev/inmo/tgbotapi/extensions/utils/ClassCastsNewKt {
public static final fun ifRequestGuestMessage (Ldev/inmo/tgbotapi/types/message/abstracts/Message;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRestrictedChatMember (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRestrictedMemberChatMember (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockAnchor (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockAnimation (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockAudio (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockBlockQuotation (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockCollage (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockDetails (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockDivider (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockFooter (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockList (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockMap (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockMathematicalExpression (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockMedia (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockParagraph (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockPhoto (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockPreformatted (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockPullQuotation (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockSectionHeading (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockSlideshow (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockTable (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockThinking (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockVideo (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichBlockVoiceNote (Ldev/inmo/tgbotapi/types/rich/RichBlock;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichMessageContent (Ldev/inmo/tgbotapi/types/message/content/ResendableContent;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextAnchor (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextAnchorLink (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextBankCardNumber (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextBold (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextBotCommand (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextCashtag (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextCode (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextCustomEmoji (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextDateTime (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextEmailAddress (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextEntity (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextGroup (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextHashtag (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextItalic (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextMarked (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextMathematicalExpression (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextMention (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextPhoneNumber (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextPlain (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextReference (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextReferenceLink (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextSpoiler (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextStrikethrough (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextSubscript (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextSuperscript (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextTextMention (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextUnderline (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifRichTextUrl (Ldev/inmo/tgbotapi/types/rich/RichText;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifSecondaryChatInviteLink (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifSecureValueIdentity (Ldev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValue;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifSecureValueWithData (Ldev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValue;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
@@ -2142,6 +2195,7 @@ public final class dev/inmo/tgbotapi/extensions/utils/ClassCastsNewKt {
public static final fun ifTelegramMediaDocument (Ldev/inmo/tgbotapi/types/media/InputPollMedia;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifTelegramMediaDocument (Ldev/inmo/tgbotapi/types/media/TelegramMedia;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifTelegramMediaFile (Ldev/inmo/tgbotapi/types/media/BaseTelegramMediaFile;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifTelegramMediaLink (Ldev/inmo/tgbotapi/types/media/InputPollOptionMedia;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifTelegramMediaLivePhoto (Ldev/inmo/tgbotapi/types/media/InputPollMedia;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifTelegramMediaLivePhoto (Ldev/inmo/tgbotapi/types/media/InputPollOptionMedia;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun ifTelegramMediaLivePhoto (Ldev/inmo/tgbotapi/types/media/TelegramMedia;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
@@ -2340,6 +2394,8 @@ public final class dev/inmo/tgbotapi/extensions/utils/ClassCastsNewKt {
public static final fun inputInvoiceMessageContentOrThrow (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;)Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputInvoiceMessageContent;
public static final fun inputLocationMessageContentOrNull (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;)Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputLocationMessageContent;
public static final fun inputLocationMessageContentOrThrow (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;)Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputLocationMessageContent;
public static final fun inputRichMessageContentOrNull (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;)Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputRichMessageContent;
public static final fun inputRichMessageContentOrThrow (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;)Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputRichMessageContent;
public static final fun inputTextMessageContentOrNull (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;)Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputTextMessageContent;
public static final fun inputTextMessageContentOrThrow (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;)Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputTextMessageContent;
public static final fun inputVenueMessageContentOrNull (Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputMessageContent;)Ldev/inmo/tgbotapi/types/InlineQueries/InputMessageContent/InputVenueMessageContent;
@@ -2364,6 +2420,8 @@ public final class dev/inmo/tgbotapi/extensions/utils/ClassCastsNewKt {
public static final fun leftChatMemberEventOrThrow (Ldev/inmo/tgbotapi/types/message/ChatEvents/abstracts/ChatEvent;)Ldev/inmo/tgbotapi/types/message/ChatEvents/LeftChatMemberEvent;
public static final fun leftChatMemberOrNull (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;)Ldev/inmo/tgbotapi/types/chat/member/LeftChatMember;
public static final fun leftChatMemberOrThrow (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;)Ldev/inmo/tgbotapi/types/chat/member/LeftChatMember;
public static final fun linkOrNull (Ldev/inmo/tgbotapi/types/media/BaseTelegramMediaFile;)Ldev/inmo/tgbotapi/types/Link;
public static final fun linkOrThrow (Ldev/inmo/tgbotapi/types/media/BaseTelegramMediaFile;)Ldev/inmo/tgbotapi/types/Link;
public static final fun liveLocationContentOrNull (Ldev/inmo/tgbotapi/types/message/content/ResendableContent;)Ldev/inmo/tgbotapi/types/message/content/LiveLocationContent;
public static final fun liveLocationContentOrThrow (Ldev/inmo/tgbotapi/types/message/content/ResendableContent;)Ldev/inmo/tgbotapi/types/message/content/LiveLocationContent;
public static final fun liveLocationOrNull (Ldev/inmo/tgbotapi/types/location/Location;)Ldev/inmo/tgbotapi/types/location/LiveLocation;
@@ -2690,6 +2748,108 @@ public final class dev/inmo/tgbotapi/extensions/utils/ClassCastsNewKt {
public static final fun restrictedChatMemberOrThrow (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;)Ldev/inmo/tgbotapi/types/chat/member/RestrictedChatMember;
public static final fun restrictedMemberChatMemberOrNull (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;)Ldev/inmo/tgbotapi/types/chat/member/RestrictedMemberChatMember;
public static final fun restrictedMemberChatMemberOrThrow (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;)Ldev/inmo/tgbotapi/types/chat/member/RestrictedMemberChatMember;
public static final fun richBlockAnchorOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockAnchor;
public static final fun richBlockAnchorOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockAnchor;
public static final fun richBlockAnimationOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockAnimation;
public static final fun richBlockAnimationOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockAnimation;
public static final fun richBlockAudioOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockAudio;
public static final fun richBlockAudioOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockAudio;
public static final fun richBlockBlockQuotationOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockBlockQuotation;
public static final fun richBlockBlockQuotationOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockBlockQuotation;
public static final fun richBlockCollageOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockCollage;
public static final fun richBlockCollageOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockCollage;
public static final fun richBlockDetailsOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockDetails;
public static final fun richBlockDetailsOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockDetails;
public static final fun richBlockDividerOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockDivider;
public static final fun richBlockDividerOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockDivider;
public static final fun richBlockFooterOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockFooter;
public static final fun richBlockFooterOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockFooter;
public static final fun richBlockListOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockList;
public static final fun richBlockListOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockList;
public static final fun richBlockMapOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockMap;
public static final fun richBlockMapOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockMap;
public static final fun richBlockMathematicalExpressionOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockMathematicalExpression;
public static final fun richBlockMathematicalExpressionOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockMathematicalExpression;
public static final fun richBlockMediaOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockMedia;
public static final fun richBlockMediaOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockMedia;
public static final fun richBlockParagraphOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockParagraph;
public static final fun richBlockParagraphOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockParagraph;
public static final fun richBlockPhotoOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockPhoto;
public static final fun richBlockPhotoOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockPhoto;
public static final fun richBlockPreformattedOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockPreformatted;
public static final fun richBlockPreformattedOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockPreformatted;
public static final fun richBlockPullQuotationOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockPullQuotation;
public static final fun richBlockPullQuotationOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockPullQuotation;
public static final fun richBlockSectionHeadingOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockSectionHeading;
public static final fun richBlockSectionHeadingOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockSectionHeading;
public static final fun richBlockSlideshowOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockSlideshow;
public static final fun richBlockSlideshowOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockSlideshow;
public static final fun richBlockTableOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockTable;
public static final fun richBlockTableOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockTable;
public static final fun richBlockThinkingOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockThinking;
public static final fun richBlockThinkingOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockThinking;
public static final fun richBlockVideoOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockVideo;
public static final fun richBlockVideoOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockVideo;
public static final fun richBlockVoiceNoteOrNull (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockVoiceNote;
public static final fun richBlockVoiceNoteOrThrow (Ldev/inmo/tgbotapi/types/rich/RichBlock;)Ldev/inmo/tgbotapi/types/rich/RichBlockVoiceNote;
public static final fun richMessageContentOrNull (Ldev/inmo/tgbotapi/types/message/content/ResendableContent;)Ldev/inmo/tgbotapi/types/message/content/RichMessageContent;
public static final fun richMessageContentOrThrow (Ldev/inmo/tgbotapi/types/message/content/ResendableContent;)Ldev/inmo/tgbotapi/types/message/content/RichMessageContent;
public static final fun richTextAnchorLinkOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextAnchorLink;
public static final fun richTextAnchorLinkOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextAnchorLink;
public static final fun richTextAnchorOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextAnchor;
public static final fun richTextAnchorOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextAnchor;
public static final fun richTextBankCardNumberOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextBankCardNumber;
public static final fun richTextBankCardNumberOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextBankCardNumber;
public static final fun richTextBoldOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextBold;
public static final fun richTextBoldOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextBold;
public static final fun richTextBotCommandOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextBotCommand;
public static final fun richTextBotCommandOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextBotCommand;
public static final fun richTextCashtagOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextCashtag;
public static final fun richTextCashtagOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextCashtag;
public static final fun richTextCodeOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextCode;
public static final fun richTextCodeOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextCode;
public static final fun richTextCustomEmojiOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextCustomEmoji;
public static final fun richTextCustomEmojiOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextCustomEmoji;
public static final fun richTextDateTimeOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextDateTime;
public static final fun richTextDateTimeOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextDateTime;
public static final fun richTextEmailAddressOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextEmailAddress;
public static final fun richTextEmailAddressOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextEmailAddress;
public static final fun richTextEntityOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextEntity;
public static final fun richTextEntityOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextEntity;
public static final fun richTextGroupOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextGroup;
public static final fun richTextGroupOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextGroup;
public static final fun richTextHashtagOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextHashtag;
public static final fun richTextHashtagOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextHashtag;
public static final fun richTextItalicOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextItalic;
public static final fun richTextItalicOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextItalic;
public static final fun richTextMarkedOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextMarked;
public static final fun richTextMarkedOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextMarked;
public static final fun richTextMathematicalExpressionOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextMathematicalExpression;
public static final fun richTextMathematicalExpressionOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextMathematicalExpression;
public static final fun richTextMentionOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextMention;
public static final fun richTextMentionOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextMention;
public static final fun richTextPhoneNumberOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextPhoneNumber;
public static final fun richTextPhoneNumberOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextPhoneNumber;
public static final fun richTextPlainOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextPlain;
public static final fun richTextPlainOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextPlain;
public static final fun richTextReferenceLinkOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextReferenceLink;
public static final fun richTextReferenceLinkOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextReferenceLink;
public static final fun richTextReferenceOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextReference;
public static final fun richTextReferenceOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextReference;
public static final fun richTextSpoilerOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextSpoiler;
public static final fun richTextSpoilerOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextSpoiler;
public static final fun richTextStrikethroughOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextStrikethrough;
public static final fun richTextStrikethroughOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextStrikethrough;
public static final fun richTextSubscriptOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextSubscript;
public static final fun richTextSubscriptOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextSubscript;
public static final fun richTextSuperscriptOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextSuperscript;
public static final fun richTextSuperscriptOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextSuperscript;
public static final fun richTextTextMentionOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextTextMention;
public static final fun richTextTextMentionOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextTextMention;
public static final fun richTextUnderlineOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextUnderline;
public static final fun richTextUnderlineOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextUnderline;
public static final fun richTextUrlOrNull (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextUrl;
public static final fun richTextUrlOrThrow (Ldev/inmo/tgbotapi/types/rich/RichText;)Ldev/inmo/tgbotapi/types/rich/RichTextUrl;
public static final fun secondaryChatInviteLinkOrNull (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;)Ldev/inmo/tgbotapi/types/SecondaryChatInviteLink;
public static final fun secondaryChatInviteLinkOrThrow (Ldev/inmo/tgbotapi/abstracts/OptionallyWithUser;)Ldev/inmo/tgbotapi/types/SecondaryChatInviteLink;
public static final fun secureValueIdentityOrNull (Ldev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValue;)Ldev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueIdentity;
@@ -2812,6 +2972,8 @@ public final class dev/inmo/tgbotapi/extensions/utils/ClassCastsNewKt {
public static final fun telegramMediaDocumentOrThrow (Ldev/inmo/tgbotapi/types/media/TelegramMedia;)Ldev/inmo/tgbotapi/types/media/TelegramMediaDocument;
public static final fun telegramMediaFileOrNull (Ldev/inmo/tgbotapi/types/media/BaseTelegramMediaFile;)Ldev/inmo/tgbotapi/types/files/TelegramMediaFile;
public static final fun telegramMediaFileOrThrow (Ldev/inmo/tgbotapi/types/media/BaseTelegramMediaFile;)Ldev/inmo/tgbotapi/types/files/TelegramMediaFile;
public static final fun telegramMediaLinkOrNull (Ldev/inmo/tgbotapi/types/media/InputPollOptionMedia;)Ldev/inmo/tgbotapi/types/media/TelegramMediaLink;
public static final fun telegramMediaLinkOrThrow (Ldev/inmo/tgbotapi/types/media/InputPollOptionMedia;)Ldev/inmo/tgbotapi/types/media/TelegramMediaLink;
public static final fun telegramMediaLivePhotoOrNull (Ldev/inmo/tgbotapi/types/media/InputPollMedia;)Ldev/inmo/tgbotapi/types/media/TelegramMediaLivePhoto;
public static final fun telegramMediaLivePhotoOrNull (Ldev/inmo/tgbotapi/types/media/InputPollOptionMedia;)Ldev/inmo/tgbotapi/types/media/TelegramMediaLivePhoto;
public static final fun telegramMediaLivePhotoOrNull (Ldev/inmo/tgbotapi/types/media/TelegramMedia;)Ldev/inmo/tgbotapi/types/media/TelegramMediaLivePhoto;
@@ -3062,6 +3224,7 @@ public final class dev/inmo/tgbotapi/extensions/utils/ContentMessageConversation
public static final fun onlyLocationContentMessages (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
public static final fun onlyPhotoContentMessages (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
public static final fun onlyPollContentMessages (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
public static final fun onlyRichMessageContentMessages (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
public static final fun onlyStickerContentMessages (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
public static final fun onlyStoryContentMessages (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
public static final fun onlyTextContentMessages (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
@@ -3255,6 +3418,7 @@ public final class dev/inmo/tgbotapi/extensions/utils/extensions/raw/CallbackQue
public final class dev/inmo/tgbotapi/extensions/utils/extensions/raw/ChatJoinRequestKt {
public static final fun getInvite_link (Ldev/inmo/tgbotapi/types/chat/ChatJoinRequest;)Ldev/inmo/tgbotapi/types/ChatInviteLink;
public static final fun getQuery_id (Ldev/inmo/tgbotapi/types/chat/ChatJoinRequest;)Ljava/lang/String;
}
public final class dev/inmo/tgbotapi/extensions/utils/extensions/raw/ChatMemberUpdatedKt {

View File

@@ -22,6 +22,7 @@ fun Flow<ContentMessage<*>>.onlyPollContentMessages() = withContentType<PollCont
fun Flow<ContentMessage<*>>.onlyStickerContentMessages() = withContentType<StickerContent>()
fun Flow<ContentMessage<*>>.onlyTextContentMessages() = withContentType<TextContent>()
fun Flow<ContentMessage<*>>.onlyStoryContentMessages() = withContentType<StoryContent>()
fun Flow<ContentMessage<*>>.onlyRichMessageContentMessages() = withContentType<RichMessageContent>()
fun Flow<ContentMessage<*>>.onlyVenueContentMessages() = withContentType<VenueContent>()
fun Flow<ContentMessage<*>>.onlyVideoContentMessages() = withContentType<VideoContent>()
fun Flow<ContentMessage<*>>.onlyLivePhotoContentMessages() = withContentType<LivePhotoContent>()

View File

@@ -1,9 +1,14 @@
package dev.inmo.tgbotapi.extensions.utils.extensions.raw
import dev.inmo.tgbotapi.types.ChatInviteLink
import dev.inmo.tgbotapi.types.ChatJoinRequestQueryId
import dev.inmo.tgbotapi.types.chat.ChatJoinRequest
import dev.inmo.tgbotapi.utils.RiskFeature
@RiskFeature(RawFieldsUsageWarning)
val ChatJoinRequest.invite_link: ChatInviteLink?
get() = inviteLink
@RiskFeature(RawFieldsUsageWarning)
val ChatJoinRequest.query_id: ChatJoinRequestQueryId?
get() = queryId