diff --git a/CHANGELOG.md b/CHANGELOG.md index 81c8c33231..6bfe372f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # TelegramBotAPI changelog +## 6.0.0 + +* `Versions`: + * `Kotlin`: `1.7.22` -> `1.8.10` + * `MicroUtils`: `0.16.10` -> `0.17.0` + * `Serialization`: `1.4.1` -> `1.5.0` + * `uuid`: `0.6.0` -> `0.7.0` +* `Core`: + * `*.link` extensions have been deprecated with renaming to avoid collisions with `link` methods +* `API`: + * Add `TelegramBot.resend` methods +* `BehaviourBuilder`: + * Add triggers and waiters for `VisualMediaGroupPartContent` +* `Utils`: + * `*.link` extensions have been deprecated with renaming to avoid collisions with `link` methods + ## 5.2.1 * `Core`: diff --git a/gradle.properties b/gradle.properties index 191322daf2..4264b00b70 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,4 @@ kotlin.incremental=true kotlin.incremental.js=true library_group=dev.inmo -library_version=5.2.1 +library_version=6.0.0 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c107f2ee30..f892ffa726 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,19 +1,19 @@ [versions] -kotlin = "1.7.22" -kotlin-serialization = "1.4.1" +kotlin = "1.8.10" +kotlin-serialization = "1.5.0" kotlin-coroutines = "1.6.4" javax-activation = "1.1.1" korlibs = "3.4.0" -uuid = "0.6.0" +uuid = "0.7.0" ktor = "2.2.3" -ksp = "1.7.22-1.0.8" +ksp = "1.8.10-1.0.9" kotlin-poet = "1.12.0" -microutils = "0.16.10" +microutils = "0.17.0" github-release-plugin = "2.4.1" dokka = "1.7.20" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c08cdc9e93..d2d5d93833 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip diff --git a/publish.gradle b/publish.gradle index c35549bf5d..0049dfc61a 100644 --- a/publish.gradle +++ b/publish.gradle @@ -1,7 +1,8 @@ apply plugin: 'maven-publish' task javadocsJar(type: Jar) { - classifier = 'javadoc' + archiveClassifier.convention("javadoc") + archiveClassifier.set("javadoc") } publishing { @@ -19,22 +20,22 @@ publishing { } developers { - + developer { id = "InsanusMokrassar" name = "Ovsiannikov Aleksei" email = "ovsyannikov.alexey95@gmail.com" } - + } licenses { - + license { name = "Apache Software License 2.0" url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE" } - + } } repositories { @@ -42,55 +43,55 @@ publishing { maven { name = "GithubPackages" url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI") - + credentials { username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER') password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD') } - + } } if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) { maven { name = "Gitea" url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven") - + credentials(HttpHeaderCredentials) { name = "Authorization" value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN') } - + authentication { header(HttpHeaderAuthentication) } - + } } if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { maven { name = "sonatype" url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") - + credentials { username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') } - + } } } } } - + if (project.hasProperty("signing.gnupg.keyName")) { apply plugin: 'signing' - + signing { useGpgCmd() - + sign publishing.publications } - + task signAll { tasks.withType(Sign).forEach { dependsOn(it) diff --git a/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/ResendMessage.kt b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/ResendMessage.kt new file mode 100644 index 0000000000..8a68ce82f7 --- /dev/null +++ b/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/ResendMessage.kt @@ -0,0 +1,108 @@ +package dev.inmo.tgbotapi.extensions.api.send + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.types.ChatIdentifier +import dev.inmo.tgbotapi.types.MessageId +import dev.inmo.tgbotapi.types.MessageThreadId +import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup +import dev.inmo.tgbotapi.types.chat.Chat +import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage +import dev.inmo.tgbotapi.types.message.content.MessageContent +import dev.inmo.tgbotapi.types.threadId + +/** + * This method will send [content] to the [chatId] as is + */ +suspend inline fun TelegramBot.resend( + chatId: ChatIdentifier, + content: T, + messageThreadId: MessageThreadId? = chatId.threadId, + disableNotification: Boolean = false, + protectContent: Boolean = false, + replyToMessageId: MessageId? = null, + allowSendingWithoutReply: Boolean? = null, + replyMarkup: KeyboardMarkup? = null +) = execute( + content.createResend( + chatId = chatId, + messageThreadId = messageThreadId, + disableNotification = disableNotification, + protectContent = protectContent, + replyToMessageId = replyToMessageId, + allowSendingWithoutReply = allowSendingWithoutReply, + replyMarkup = replyMarkup + ) +) as ContentMessage + +/** + * This method will send [content] to the [chatId] as is + */ +suspend inline fun TelegramBot.resend( + chat: Chat, + content: T, + messageThreadId: MessageThreadId? = chat.id.threadId, + disableNotification: Boolean = false, + protectContent: Boolean = false, + replyToMessageId: MessageId? = null, + allowSendingWithoutReply: Boolean? = null, + replyMarkup: KeyboardMarkup? = null +) = resend( + chatId = chat.id, + content = content, + messageThreadId = messageThreadId, + disableNotification = disableNotification, + protectContent = protectContent, + replyToMessageId = replyToMessageId, + allowSendingWithoutReply = allowSendingWithoutReply, + replyMarkup = replyMarkup +) + +/** + * This method will send [message] content to the [chatId]. In difference with [copyMessage], this method will use + * native methods for data sending (like [dev.inmo.tgbotapi.extensions.api.send.media.sendPhoto] if inoming content is + * [dev.inmo.tgbotapi.types.message.content.PhotoContent]) + */ +suspend inline fun TelegramBot.resend( + chatId: ChatIdentifier, + message: ContentMessage, + messageThreadId: MessageThreadId? = chatId.threadId, + disableNotification: Boolean = false, + protectContent: Boolean = false, + replyToMessageId: MessageId? = null, + allowSendingWithoutReply: Boolean? = null, + replyMarkup: KeyboardMarkup? = null +) = resend( + chatId = chatId, + content = message.content, + messageThreadId = messageThreadId, + disableNotification = disableNotification, + protectContent = protectContent, + replyToMessageId = replyToMessageId, + allowSendingWithoutReply = allowSendingWithoutReply, + replyMarkup = replyMarkup +) + +/** + * This method will send [message] content to the [chat]. In difference with [copyMessage], this method will use + * native methods for data sending (like [dev.inmo.tgbotapi.extensions.api.send.media.sendPhoto] if inoming content is + * [dev.inmo.tgbotapi.types.message.content.PhotoContent]) + */ +suspend inline fun TelegramBot.resend( + chat: Chat, + message: ContentMessage, + messageThreadId: MessageThreadId? = chat.id.threadId, + disableNotification: Boolean = false, + protectContent: Boolean = false, + replyToMessageId: MessageId? = null, + allowSendingWithoutReply: Boolean? = null, + replyMarkup: KeyboardMarkup? = null +) = resend( + chatId = chat.id, + message = message, + messageThreadId = messageThreadId, + disableNotification = disableNotification, + protectContent = protectContent, + replyToMessageId = replyToMessageId, + allowSendingWithoutReply = allowSendingWithoutReply, + replyMarkup = replyMarkup +) diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContent.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContent.kt index eed6617247..c5e706be4b 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContent.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContent.kt @@ -189,3 +189,7 @@ suspend fun BehaviourContext.waitInvoice( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } ) = waitContent(initRequest, errorFactory) +suspend fun BehaviourContext.waitVisualContent( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitContent(initRequest, errorFactory) diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContentMessage.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContentMessage.kt index 8f0c069aae..08ccca9aa5 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContentMessage.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/expectations/WaitContentMessage.kt @@ -216,3 +216,8 @@ suspend fun BehaviourContext.waitInvoiceMessage( initRequest: Request<*>? = null, errorFactory: NullableRequestBuilder<*> = { null } ) = waitContentMessage(initRequest, errorFactory) + +suspend fun BehaviourContext.waitVisualContentMessage( + initRequest: Request<*>? = null, + errorFactory: NullableRequestBuilder<*> = { null } +) = waitContentMessage(initRequest, errorFactory) diff --git a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt index d83be5e0f5..e070df87f3 100644 --- a/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt +++ b/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/ContentTriggers.kt @@ -607,3 +607,27 @@ suspend fun BC.onInvoice( markerFactory, 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] 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 + */ +suspend fun BC.onVisualContent( + initialFilter: CommonMessageFilter? = null, + subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver = MessageFilterByChat, + markerFactory: MarkerFactory = ByChatMessageMarkerFactory, + scenarioReceiver: CustomBehaviourContextAndTypeReceiver +) = onContentMessageWithType( + initialFilter, + subcontextUpdatesFilter, + markerFactory, + scenarioReceiver +) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/ChatIdentifier.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/ChatIdentifier.kt index 9165779964..872bda30e3 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/ChatIdentifier.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/ChatIdentifier.kt @@ -74,8 +74,11 @@ val Identifier.userLink: String @Warning("This API have restrictions in Telegram System") val UserId.userLink: String get() = chatId.userLink -val User.link: String +val User.userLink: String get() = id.userLink +@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.userLink", "dev.inmo.tgbotapi.types.userLink")) +val User.link: String + get() = userLink typealias UserId = ChatId diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/formatting/LinksFormatting.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/formatting/LinksFormatting.kt index 06fdef0e89..bb354096f3 100644 --- a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/formatting/LinksFormatting.kt +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/formatting/LinksFormatting.kt @@ -18,10 +18,16 @@ fun makeChatLink(identifier: Identifier, threadId: MessageThreadId? = null) = id fun makeUsernameDeepLinkPrefix(username: String) = "${makeUsernameLink(username)}?start=" fun makeUsernameStartattachPrefix(username: String) = "$internalLinkBeginning/$username?startattach" fun makeUsernameStartattachLink(username: String, data: String? = null) = "${makeUsernameStartattachPrefix(username)}${data?.let { "=$it" } ?: ""}" -inline val Username.link +inline val Username.usernameLink get() = makeUsernameLink(usernameWithoutAt) -val IdChatIdentifier.link: String +@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.usernameLink", "dev.inmo.tgbotapi.extensions.utils.formatting.usernameLink")) +inline val Username.link + get() = usernameLink +val IdChatIdentifier.chatLink: String get() = makeChatLink(chatId, threadId) +@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.chatLink", "dev.inmo.tgbotapi.extensions.utils.formatting.chatLink")) +val IdChatIdentifier.link: String + get() = chatLink fun ChatId.link(threadId: MessageThreadId?) = makeChatLink(chatId, threadId) inline fun Username.link(threadId: MessageThreadId?) = makeUsernameLink(usernameWithoutAt, threadId) inline val Username.deepLinkPrefix @@ -83,20 +89,27 @@ fun makeLinkToMessage( /** * @see makeLinkToMessage */ -val Message.link: String? +val Message.messageLink: String? get() = makeLinkToMessage( chat, messageId ) +/** + * @see makeLinkToMessage + */ +@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.messageLink", "dev.inmo.tgbotapi.extensions.utils.formatting.messageLink")) +val Message.link: String? + get() = messageLink + /** * Link which can be used as by any user to get access to [Chat]. Returns null in case when there are no * known way to build link */ -val Chat.link: String? +val Chat.chatLink: String? get() { if (this is UsernameChat) { - username ?.link ?: id.link + username ?.usernameLink ?: id.chatLink } if (this is ExtendedPublicChat) { inviteLink ?.let { return it } @@ -107,6 +120,14 @@ val Chat.link: String? return null } +/** + * Link which can be used as by any user to get access to [Chat]. Returns null in case when there are no + * known way to build link + */ +@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.chatLink", "dev.inmo.tgbotapi.extensions.utils.formatting.chatLink")) +val Chat.link: String? + get() = chatLink + private const val stickerSetAddingLinkPrefix = "$internalLinkBeginning/addstickers" val StickerSetName.stickerSetLink