diff --git a/CHANGELOG.md b/CHANGELOG.md index f206cda756..122e6c1ac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # TelegramBotAPI changelog +## 0.35.8 + +* `Common`: + * `Version`: + * `MicroUtils`: `0.5.24` -> `0.5.25` + * `UUID`: `0.3.0` -> `0.3.1` +* `Core`: + * `MultipartRequestCallFactory` now will use file name as multipart `filename` parameter instead of generated + filename + * New extension `MPPFile#asMultipartFile` +* `API` + * Fixes in `TelegramBot#withAction` +* `Behaviour Builder`: + * New extensions `BehaviourContext#commandWithArgs` and `BehaviourContext#onCommandWithArgs` + ## 0.35.7 * `Common`: diff --git a/gradle.properties b/gradle.properties index e22eff9a7a..b6933b5929 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,14 +9,14 @@ kotlin_version=1.5.30 kotlin_coroutines_version=1.5.2 kotlin_serialisation_runtime_version=1.2.2 klock_version=2.4.1 -uuid_version=0.3.0 +uuid_version=0.3.1 ktor_version=1.6.3 -micro_utils_version=0.5.24 +micro_utils_version=0.5.25 javax_activation_version=1.1.1 library_group=dev.inmo -library_version=0.35.7 +library_version=0.35.8 github_release_plugin_version=2.2.12 diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/MultipartRequestCallFactory.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/MultipartRequestCallFactory.kt index 2d42d15ea9..fab01c75fb 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/MultipartRequestCallFactory.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/MultipartRequestCallFactory.kt @@ -24,7 +24,7 @@ class MultipartRequestCallFactory : AbstractRequestCallFactory() { is MultipartFile -> appendInput( key, Headers.build { - append(HttpHeaders.ContentDisposition, "filename=${value.fileId}") + append(HttpHeaders.ContentDisposition, "filename=${value.filename}") }, block = value.file::input ) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt index b306112118..834437b25f 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt @@ -1,13 +1,26 @@ package dev.inmo.tgbotapi.requests.abstracts +import dev.inmo.micro_utils.common.MPPFile import dev.inmo.tgbotapi.utils.* import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.core.Input import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +/** + * Common type for all files in Telegram Bot API which can be sent via requests like [dev.inmo.tgbotapi.requests.send.media.SendDocument]. + * You may use methods like [MPPFile.asMultipartFile] when you want to send files from your file system, but you should + * remember about [restrictions][https://core.telegram.org/bots/api#sending-files] in Telegram for bots. In case you + * wish to send file by its url, use [FileId] and pass your url as [FileId.fileId] + * + * @see MPPFile.asMultipartFile + * @see ByteArray.asMultipartFile + * @see ByteReadChannel.asMultipartFile + * @see ByteReadChannelAllocator.asMultipartFile + */ @Serializable(InputFileSerializer::class) sealed class InputFile { abstract val fileId: String @@ -75,3 +88,5 @@ suspend inline fun ByteReadChannel.asMultipartFile( suspend inline fun ByteReadChannelAllocator.asMultipartFile( fileName: String ) = this.invoke().asMultipartFile(fileName) + +expect suspend fun MPPFile.asMultipartFile(): MultipartFile diff --git a/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt b/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt new file mode 100644 index 0000000000..b3cd16f998 --- /dev/null +++ b/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt @@ -0,0 +1,8 @@ +package dev.inmo.tgbotapi.requests.abstracts + +import dev.inmo.micro_utils.common.* +import io.ktor.utils.io.ByteReadChannel + +actual suspend fun MPPFile.asMultipartFile(): MultipartFile = ByteReadChannel(bytes()).asMultipartFile( + filename.name +) diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt new file mode 100644 index 0000000000..bc2ab9874e --- /dev/null +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt @@ -0,0 +1,5 @@ +package dev.inmo.tgbotapi.requests.abstracts + +import dev.inmo.micro_utils.common.MPPFile + +actual suspend fun MPPFile.asMultipartFile(): MultipartFile = toInputFile() diff --git a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/SendActionDSL.kt b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/SendActionDSL.kt index 652eaf6ba4..024abedfff 100644 --- a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/SendActionDSL.kt +++ b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/send/SendActionDSL.kt @@ -1,13 +1,13 @@ package dev.inmo.tgbotapi.extensions.api.send -import dev.inmo.micro_utils.coroutines.safely -import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.* import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.requests.send.SendAction import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.actions.* import dev.inmo.tgbotapi.types.chat.abstracts.Chat import kotlinx.coroutines.* +import kotlin.coroutines.coroutineContext private const val refreshTime: MilliSeconds = (botActionActualityTime - 1) * 1000L typealias TelegramBotActionCallback = suspend TelegramBot.() -> T @@ -16,21 +16,17 @@ suspend fun TelegramBot.withAction( actionRequest: SendAction, block: TelegramBotActionCallback ): T { - val botActionJob = supervisorScope { - launch { - while (isActive) { - delay(refreshTime) - safelyWithoutExceptions { - execute(actionRequest) - } + val botActionJob = CoroutineScope(coroutineContext).launch { + while (isActive) { + delay(refreshTime) + safelyWithoutExceptions { + execute(actionRequest) } } } - return try { - safely { block() } - } finally { - botActionJob.cancel() - } + val result = safelyWithResult { block() } + botActionJob.cancel() + return result.getOrThrow() } suspend fun TelegramBot.withAction( diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt index c92ff63c62..35fab43c78 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.filter typealias BehaviourContextReceiver = suspend BehaviourContext.() -> T typealias BehaviourContextAndTypeReceiver = suspend BehaviourContext.(I) -> T +typealias BehaviourContextAndTwoTypesReceiver = suspend BehaviourContext.(I1, I2) -> T /** * This class contains all necessary tools for work with bots and especially for [buildBehaviour] diff --git a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/CommandHandling.kt b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/CommandHandling.kt index 9e90a3cd70..fa81c759df 100644 --- a/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/CommandHandling.kt +++ b/tgbotapi.extensions.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/triggers_handling/CommandHandling.kt @@ -1,10 +1,10 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTypeReceiver +import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.ByChatMessageMarkerFactory import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory import dev.inmo.tgbotapi.extensions.utils.asBotCommandTextSource +import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.message.content.TextContent import kotlinx.coroutines.Job @@ -33,6 +33,7 @@ suspend fun BehaviourContext.command( markerFactory, scenarioReceiver ) + suspend fun BehaviourContext.command( command: String, requireOnlyCommandInMessage: Boolean = true, @@ -59,3 +60,53 @@ suspend inline fun BehaviourContext.onCommand( markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, noinline scenarioReceiver: BehaviourContextAndTypeReceiver> ): Job = onCommand(command.toRegex(), requireOnlyCommandInMessage, includeFilterByChatInBehaviourSubContext, additionalFilter, markerFactory, scenarioReceiver) + +suspend fun BehaviourContext.commandWithArgs( + commandRegex: Regex, + includeFilterByChatInBehaviourSubContext: Boolean = true, + additionalFilter: CommonMessageFilter? = null, + markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, + scenarioReceiver: BehaviourContextAndTwoTypesReceiver, Array> +) = command( + commandRegex, + requireOnlyCommandInMessage = false, + includeFilterByChatInBehaviourSubContext = includeFilterByChatInBehaviourSubContext, + additionalFilter = additionalFilter, + markerFactory = markerFactory +) { + val args = it.parseCommandsWithParams().let { commandsWithArgs -> + val key = commandsWithArgs.keys.firstOrNull { it.matches(commandRegex) } ?: return@let null + commandsWithArgs[key] + } ?: emptyArray() + scenarioReceiver(it, args) +} + +suspend fun BehaviourContext.commandWithArgs( + command: String, + includeFilterByChatInBehaviourSubContext: Boolean = true, + additionalFilter: CommonMessageFilter? = null, + markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, + scenarioReceiver: BehaviourContextAndTwoTypesReceiver, Array> +) = commandWithArgs( + command.toRegex(), + includeFilterByChatInBehaviourSubContext = includeFilterByChatInBehaviourSubContext, + additionalFilter = additionalFilter, + markerFactory = markerFactory, + scenarioReceiver = scenarioReceiver +) + +suspend inline fun BehaviourContext.onCommandWithArgs( + commandRegex: Regex, + includeFilterByChatInBehaviourSubContext: Boolean = true, + noinline additionalFilter: CommonMessageFilter? = null, + markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, + noinline scenarioReceiver: BehaviourContextAndTwoTypesReceiver, Array> +): Job = commandWithArgs(commandRegex, includeFilterByChatInBehaviourSubContext, additionalFilter, markerFactory, scenarioReceiver) + +suspend inline fun BehaviourContext.onCommandWithArgs( + command: String, + includeFilterByChatInBehaviourSubContext: Boolean = true, + noinline additionalFilter: CommonMessageFilter? = null, + markerFactory: MarkerFactory, Any> = ByChatMessageMarkerFactory, + noinline scenarioReceiver: BehaviourContextAndTwoTypesReceiver, Array> +): Job = onCommandWithArgs(command.toRegex(), includeFilterByChatInBehaviourSubContext, additionalFilter, markerFactory, scenarioReceiver) diff --git a/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/shortcuts/RequestsExecutor.kt b/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/shortcuts/RequestsExecutor.kt index f3ba0f62e7..f71350ad97 100644 --- a/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/shortcuts/RequestsExecutor.kt +++ b/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/shortcuts/RequestsExecutor.kt @@ -4,6 +4,7 @@ import dev.inmo.micro_utils.coroutines.safely import dev.inmo.tgbotapi.bot.RequestsExecutor import dev.inmo.tgbotapi.requests.abstracts.Request import kotlinx.coroutines.* +import kotlin.coroutines.coroutineContext fun RequestsExecutor.executeAsync( request: Request, @@ -16,9 +17,7 @@ fun RequestsExecutor.executeAsync( suspend fun RequestsExecutor.executeAsync( request: Request -): Deferred = coroutineScope { - executeAsync(request, this) -} +): Deferred = executeAsync(request, CoroutineScope(coroutineContext)) suspend fun RequestsExecutor.executeUnsafe( request: Request,