From 8b92a678fa222ccb7ef4185c91c2d73ceca504c7 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 20 Apr 2019 12:17:03 +0800 Subject: [PATCH 01/12] start 0.14.0 --- CHANGELOG.md | 2 ++ build.gradle | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a14abb39f..c7f3264e0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # TelegramBotAPI changelog +## 0.14.0 + ## 0.13.0 Telegram Polls * Type `PollOption` and `AnonymousPollOption` added diff --git a/build.gradle b/build.gradle index e6e1a82bd5..d34c7e57b7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -project.version = "0.13.0" +project.version = "0.14.0" project.group = "com.github.insanusmokrassar" buildscript { From 6d8190e5aaf0cda6534c6cb791f61cb30e925092 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 20 Apr 2019 12:55:11 +0800 Subject: [PATCH 02/12] commonly of webhooks and other requests-related code --- CHANGELOG.md | 5 +++ build.gradle | 11 ++----- gradle.properties | 2 +- .../insanusmokrassar/TelegramBotAPI/Index.kt | 16 ---------- .../bot/Ktor/KtorRequestsExecutor.kt | 7 ++-- .../TelegramBotAPI/bot/Ktor/ProxySettings.kt | 32 ------------------- .../TelegramBotAPI/bot/ProxySettings.kt | 18 ----------- .../utils/extensions/Webhooks.kt | 30 ++++++++--------- 8 files changed, 25 insertions(+), 96 deletions(-) delete mode 100644 src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/Index.kt delete mode 100644 src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/ProxySettings.kt delete mode 100644 src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/ProxySettings.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index c7f3264e0f..da0ff57509 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 0.14.0 +* Now library have no default engine for both webhooks and requests executor. It is required for clients to set +some default library +* All proxy help methods was removed . They are will be replaced in separated project +* `Ktor` version `1.1.3` -> `1.1.4` + ## 0.13.0 Telegram Polls * Type `PollOption` and `AnonymousPollOption` added diff --git a/build.gradle b/build.gradle index d34c7e57b7..a7ed40969e 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,6 @@ repositories { jcenter() mavenCentral() maven { url "https://kotlin.bintray.com/kotlinx" } - maven { url "https://dl.bintray.com/kotlin/ktor" } } dependencies { @@ -35,14 +34,10 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlin_serialisation_runtime_version" implementation "joda-time:joda-time:$joda_time_version" - implementation "io.ktor:ktor-client-core:$ktor_version" - implementation "io.ktor:ktor-client-okhttp:$ktor_version" + implementation "io.ktor:ktor-client:$ktor_version" - implementation "io.ktor:ktor-server-core:$ktor_version" - implementation "io.ktor:ktor-server-netty:$ktor_version" - - // Use JUnit test framework - testImplementation 'junit:junit:4.12' + implementation "io.ktor:ktor-server:$ktor_version" + implementation "io.ktor:ktor-server-host-common:$ktor_version" } compileKotlin { diff --git a/gradle.properties b/gradle.properties index 43c7278bdd..7f46ad5b74 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ kotlin_version=1.3.30 kotlin_coroutines_version=1.2.0 kotlin_serialisation_runtime_version=0.11.0 joda_time_version=2.10.1 -ktor_version=1.1.3 +ktor_version=1.1.4 gradle_bintray_plugin_version=1.8.4 diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/Index.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/Index.kt deleted file mode 100644 index bf1c66aab4..0000000000 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/Index.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.insanusmokrassar.TelegramBotAPI - -import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.KtorRequestsExecutor -import io.ktor.client.engine.okhttp.OkHttp -import kotlinx.coroutines.runBlocking - -fun main(args: Array) { - runBlocking { - KtorRequestsExecutor( - args[0], - OkHttp.create() - ).apply { - // It is just template of creating requests executor - } - } -} diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt index 285bf033e9..448fada318 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt @@ -12,7 +12,6 @@ import com.github.insanusmokrassar.TelegramBotAPI.types.RetryAfterError import io.ktor.client.HttpClient import io.ktor.client.call.HttpClientCall import io.ktor.client.engine.HttpClientEngine -import io.ktor.client.engine.okhttp.OkHttp import io.ktor.util.cio.toByteArray import kotlinx.coroutines.delay import kotlinx.io.charsets.Charset @@ -20,7 +19,7 @@ import kotlinx.serialization.json.Json class KtorRequestsExecutor( token: String, - private val client: HttpClient = HttpClient(OkHttp), + private val client: HttpClient = HttpClient(), hostUrl: String = "https://api.telegram.org", callsFactories: List = emptyList(), excludeDefaultFactories: Boolean = false, @@ -29,11 +28,11 @@ class KtorRequestsExecutor( ) : BaseRequestsExecutor(token, hostUrl) { constructor( token: String, - engine: HttpClientEngine = OkHttp.create(), + engine: HttpClientEngine? = null, hostUrl: String = "https://api.telegram.org" ) : this( token, - HttpClient(engine), + engine ?.let { HttpClient(engine) } ?: HttpClient(), hostUrl ) diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/ProxySettings.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/ProxySettings.kt deleted file mode 100644 index 745c4ecdf8..0000000000 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/ProxySettings.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor - -import com.github.insanusmokrassar.TelegramBotAPI.bot.settings.ProxySettings -import io.ktor.http.HttpHeaders -import okhttp3.Credentials -import okhttp3.OkHttpClient -import java.net.InetSocketAddress -import java.net.Proxy - -fun OkHttpClient.Builder.useWith(proxySettings: ProxySettings) { - proxy( - Proxy( - Proxy.Type.SOCKS, - InetSocketAddress( - proxySettings.host, - proxySettings.port - ) - ) - ) - proxySettings.password ?.let { - password -> - proxyAuthenticator { - _, response -> - response.request().newBuilder().apply { - addHeader( - HttpHeaders.ProxyAuthorization, - Credentials.basic(proxySettings.username ?: "", password) - ) - }.build() - } - } -} diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/ProxySettings.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/ProxySettings.kt deleted file mode 100644 index 12e3ab9e71..0000000000 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/ProxySettings.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.github.insanusmokrassar.TelegramBotAPI.bot - -import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.useWith -import com.github.insanusmokrassar.TelegramBotAPI.bot.settings.ProxySettings -import okhttp3.OkHttpClient - -@Deprecated( - "Replaced in settings package", - ReplaceWith("ProxySettings", "com.github.insanusmokrassar.TelegramBotAPI.bot.settings.ProxySettings") -) -typealias ProxySettings = com.github.insanusmokrassar.TelegramBotAPI.bot.settings.ProxySettings - - -@Deprecated( - "Replaced in Ktor package", - ReplaceWith("useWith", "com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.useWith") -) -fun OkHttpClient.Builder.useWith(proxySettings: ProxySettings) = useWith(proxySettings) diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/Webhooks.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/Webhooks.kt index 7d0af080a7..c198a22872 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/Webhooks.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/Webhooks.kt @@ -16,7 +16,6 @@ import io.ktor.response.respond import io.ktor.routing.post import io.ktor.routing.routing import io.ktor.server.engine.* -import io.ktor.server.netty.Netty import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.serialization.json.Json @@ -36,12 +35,12 @@ import java.util.concurrent.TimeUnit suspend fun RequestsExecutor.setWebhook( url: String, port: Int, + engineFactory: ApplicationEngineFactory<*, *>, certificate: InputFile? = null, privateKeyConfig: WebhookPrivateKeyConfig? = null, scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()), allowedUpdates: List? = null, maxAllowedConnections: Int? = null, - engineFactory: ApplicationEngineFactory<*, *> = Netty, block: UpdateReceiver ): Job { val executeDeferred = certificate ?.let { @@ -69,20 +68,17 @@ suspend fun RequestsExecutor.setWebhook( val env = applicationEngineEnvironment { module { - fun Application.main() { - routing { - post { - val deserialized = call.receiveText() - val update = Json.nonstrict.parse( - RawUpdate.serializer(), - deserialized - ) - updatesChannel.send(update.asUpdate) - call.respond("Ok") - } + routing { + post { + val deserialized = call.receiveText() + val update = Json.nonstrict.parse( + RawUpdate.serializer(), + deserialized + ) + updatesChannel.send(update.asUpdate) + call.respond("Ok") } } - main() } privateKeyConfig ?.let { sslConnector( @@ -140,19 +136,19 @@ suspend fun RequestsExecutor.setWebhook( url: String, port: Int, filter: UpdatesFilter, + engineFactory: ApplicationEngineFactory<*, *>, certificate: InputFile? = null, privateKeyConfig: WebhookPrivateKeyConfig? = null, scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()), - maxAllowedConnections: Int? = null, - engineFactory: ApplicationEngineFactory<*, *> = Netty + maxAllowedConnections: Int? = null ): Job = setWebhook( url, port, + engineFactory, certificate, privateKeyConfig, scope, filter.allowedUpdates, maxAllowedConnections, - engineFactory, filter.asUpdateReceiver ) From b002f61b7182d67fff95f46dcb9d6699c22a91ed Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 20 Apr 2019 13:09:47 +0800 Subject: [PATCH 03/12] add serializer to InlineKeyboardButton and remove exact serializer for InlineKeyboardMarkup#keyboard --- .../buttons/InlineKeyboardButtons/InlineKeyboardButton.kt | 7 ++++++- .../TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt | 5 ----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardButtons/InlineKeyboardButton.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardButtons/InlineKeyboardButton.kt index cf1bfc63b2..35741de405 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardButtons/InlineKeyboardButton.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardButtons/InlineKeyboardButton.kt @@ -1,5 +1,10 @@ package com.github.insanusmokrassar.TelegramBotAPI.types.buttons.InlineKeyboardButtons +import kotlinx.serialization.* + +@Serializable(InlineKeyboardButtonSerializer::class) interface InlineKeyboardButton { val text: String -} \ No newline at end of file +} + +object InlineKeyboardButtonSerializer : KSerializer by ContextSerializer(InlineKeyboardButton::class) diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt index 39c5bea691..1e3185ccd5 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt @@ -7,10 +7,5 @@ import kotlinx.serialization.internal.ArrayListSerializer @Serializable data class InlineKeyboardMarkup( @SerialName("inline_keyboard") - @Serializable(with = KeyboardSerializer::class) val keyboard: Matrix ) : KeyboardMarkup - -object KeyboardSerializer : KSerializer> by ArrayListSerializer( - ArrayListSerializer(ContextSerializer(InlineKeyboardButton::class)) -) From bed60720d33a4c01148579064399a94b6247e425 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 20 Apr 2019 13:14:49 +0800 Subject: [PATCH 04/12] InlineKeyboardMarkup using field as serial name for keyboard --- .../com/github/insanusmokrassar/TelegramBotAPI/types/Common.kt | 1 + .../TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/Common.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/Common.kt index 9122be4efe..055e4b0cd0 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/Common.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/Common.kt @@ -64,6 +64,7 @@ const val inlineMessageIdField = "inline_message_id" const val callbackDataField = "callback_data" const val callbackQueryIdField = "callback_query_id" const val inlineQueryIdField = "inline_query_id" +const val inlineKeyboardField = "inline_keyboard" const val showAlertField = "show_alert" const val cachedTimeField = "cached_time" const val foursquareIdField = "foursquare_id" diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt index 1e3185ccd5..a5ff3ee61e 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/buttons/InlineKeyboardMarkup.kt @@ -1,11 +1,12 @@ package com.github.insanusmokrassar.TelegramBotAPI.types.buttons import com.github.insanusmokrassar.TelegramBotAPI.types.buttons.InlineKeyboardButtons.InlineKeyboardButton +import com.github.insanusmokrassar.TelegramBotAPI.types.inlineKeyboardField import kotlinx.serialization.* import kotlinx.serialization.internal.ArrayListSerializer @Serializable data class InlineKeyboardMarkup( - @SerialName("inline_keyboard") + @SerialName(inlineKeyboardField) val keyboard: Matrix ) : KeyboardMarkup From 711410c42655edae317be097b5c4bd1ff8a2afaf Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 26 Apr 2019 10:34:32 +0800 Subject: [PATCH 05/12] force UTF-8 usage for responses decoding --- CHANGELOG.md | 1 + .../TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da0ff57509..7c80766de6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ some default library * All proxy help methods was removed . They are will be replaced in separated project * `Ktor` version `1.1.3` -> `1.1.4` +* Requests results now always decoding as `UTF-8` ## 0.13.0 Telegram Polls diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt index 448fada318..42827316ae 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt @@ -60,7 +60,7 @@ class KtorRequestsExecutor( if (call == null) { throw IllegalArgumentException("Can't execute request: $request") } - val content = call.response.content.toByteArray().toString(Charset.defaultCharset()) + val content = call.response.content.toByteArray().toString(Charsets.UTF_8) val responseObject = jsonFormatter.parse( Response.serializer(request.resultSerializer()), content From c12d08eda82c98cc203cfe1720e1e0ad71cf2486 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 26 Apr 2019 11:12:01 +0800 Subject: [PATCH 06/12] refactor of Ktor calls --- CHANGELOG.md | 2 + .../bot/Ktor/KtorRequestsExecutor.kt | 4 +- .../Ktor/base/AbstractRequestCallFactory.kt | 41 ++++++++++++++ .../Ktor/base/MultipartRequestCallFactory.kt | 53 ++++++++----------- .../bot/Ktor/base/SimpleRequestCallFactory.kt | 31 ++++------- 5 files changed, 76 insertions(+), 55 deletions(-) create mode 100644 src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/AbstractRequestCallFactory.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c80766de6..5206e921af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ some default library * All proxy help methods was removed . They are will be replaced in separated project * `Ktor` version `1.1.3` -> `1.1.4` * Requests results now always decoding as `UTF-8` +* `AbstractRequestCallFactory` was added with cache of methods urls to avoid memory leaks +* Small refactoring of work with response in `KtorRequestsExecutor` ## 0.13.0 Telegram Polls diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt index 42827316ae..cc4e3a2643 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt @@ -60,7 +60,9 @@ class KtorRequestsExecutor( if (call == null) { throw IllegalArgumentException("Can't execute request: $request") } - val content = call.response.content.toByteArray().toString(Charsets.UTF_8) + val content = call.response.use { + it.content.toByteArray().toString(Charsets.UTF_8) + } val responseObject = jsonFormatter.parse( Response.serializer(request.resultSerializer()), content diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/AbstractRequestCallFactory.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/AbstractRequestCallFactory.kt new file mode 100644 index 0000000000..5076d173d2 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/AbstractRequestCallFactory.kt @@ -0,0 +1,41 @@ +package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base + +import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.KtorCallFactory +import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request +import io.ktor.client.HttpClient +import io.ktor.client.call.HttpClientCall +import io.ktor.client.call.call +import io.ktor.client.request.accept +import io.ktor.client.request.url +import io.ktor.http.ContentType +import io.ktor.http.HttpMethod + +abstract class AbstractRequestCallFactory : KtorCallFactory { + private val methodsCache: MutableMap = mutableMapOf() + override suspend fun prepareCall( + client: HttpClient, + baseUrl: String, + request: Request + ): HttpClientCall? { + val preparedBody = prepareCallBody(client, baseUrl, request) ?: return null + + return client.call { + url( + methodsCache[request.method()] ?: "$baseUrl/${request.method()}".also { + methodsCache[request.method()] = it + } + ) + method = HttpMethod.Post + accept(ContentType.Application.Json) + + body = preparedBody + build() + } + } + + protected abstract fun prepareCallBody( + client: HttpClient, + baseUrl: String, + request: Request + ): Any? +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/MultipartRequestCallFactory.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/MultipartRequestCallFactory.kt index e7c16717e7..2f2e1015f0 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/MultipartRequestCallFactory.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/MultipartRequestCallFactory.kt @@ -1,48 +1,37 @@ package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base -import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.KtorCallFactory import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.* import com.github.insanusmokrassar.TelegramBotAPI.utils.mapWithCommonValues import io.ktor.client.HttpClient -import io.ktor.client.call.HttpClientCall -import io.ktor.client.call.call -import io.ktor.client.request.accept import io.ktor.client.request.forms.MultiPartFormDataContent import io.ktor.client.request.forms.formData -import io.ktor.client.request.url import io.ktor.http.* -class MultipartRequestCallFactory : KtorCallFactory { - override suspend fun prepareCall( +class MultipartRequestCallFactory : AbstractRequestCallFactory() { + + override fun prepareCallBody( client: HttpClient, baseUrl: String, request: Request - ): HttpClientCall? = (request as? MultipartRequest) ?.let { - castedRequest -> - client.call { - url("$baseUrl/${castedRequest.method()}") - method = HttpMethod.Post - accept(ContentType.Application.Json) - body = MultiPartFormDataContent( - formData { - val params = castedRequest.paramsJson.mapWithCommonValues() - for ((key, value) in castedRequest.mediaMap + params) { - when (value) { - is MultipartFile -> append( - key, - value.file.asInput(), - Headers.build { - append(HttpHeaders.ContentType, value.mimeType) - append(HttpHeaders.ContentDisposition, "filename=${value.fileId}") - } - ) - is FileId -> append(key, value.fileId) - else -> append(key, value.toString()) - } + ): Any? = (request as? MultipartRequest) ?.let { castedRequest -> + MultiPartFormDataContent( + formData { + val params = castedRequest.paramsJson.mapWithCommonValues() + for ((key, value) in castedRequest.mediaMap + params) { + when (value) { + is MultipartFile -> append( + key, + value.file.asInput(), + Headers.build { + append(HttpHeaders.ContentType, value.mimeType) + append(HttpHeaders.ContentDisposition, "filename=${value.fileId}") + } + ) + is FileId -> append(key, value.fileId) + else -> append(key, value.toString()) } } - ) - build() - } + } + ) } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/SimpleRequestCallFactory.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/SimpleRequestCallFactory.kt index 3e91948a34..94a72f0f01 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/SimpleRequestCallFactory.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/base/SimpleRequestCallFactory.kt @@ -1,36 +1,23 @@ package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base -import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.KtorCallFactory import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.* import com.github.insanusmokrassar.TelegramBotAPI.utils.toJsonWithoutNulls import io.ktor.client.HttpClient -import io.ktor.client.call.HttpClientCall -import io.ktor.client.call.call -import io.ktor.client.request.accept -import io.ktor.client.request.url import io.ktor.http.ContentType -import io.ktor.http.HttpMethod import io.ktor.http.content.TextContent -class SimpleRequestCallFactory : KtorCallFactory { - override suspend fun prepareCall( +class SimpleRequestCallFactory : AbstractRequestCallFactory() { + override fun prepareCallBody( client: HttpClient, baseUrl: String, request: Request - ): HttpClientCall? = (request as? SimpleRequest) ?.let { - castedRequest -> - client.call { - url("$baseUrl/${castedRequest.method()}") - method = HttpMethod.Post - accept(ContentType.Application.Json) + ): Any? = (request as? SimpleRequest) ?.let { _ -> + val content = request.toJsonWithoutNulls(SimpleRequestSerializer).toString() - val content = request.toJsonWithoutNulls(SimpleRequestSerializer).toString() - - body = TextContent( - content, - ContentType.Application.Json - ) - build() - } + TextContent( + content, + ContentType.Application.Json + ) } + } \ No newline at end of file From eb22bc5a3480de2e760f5637188f8162e6fee071 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 26 Apr 2019 14:09:45 +0800 Subject: [PATCH 07/12] update Kotlin version --- CHANGELOG.md | 1 + gradle.properties | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5206e921af..04e3fd93c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ some default library * Requests results now always decoding as `UTF-8` * `AbstractRequestCallFactory` was added with cache of methods urls to avoid memory leaks * Small refactoring of work with response in `KtorRequestsExecutor` +* Kotlin version `1.3.30` -> `1.3.31` ## 0.13.0 Telegram Polls diff --git a/gradle.properties b/gradle.properties index 7f46ad5b74..d341c6ba14 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ kotlin.code.style=official -kotlin_version=1.3.30 +kotlin_version=1.3.31 kotlin_coroutines_version=1.2.0 kotlin_serialisation_runtime_version=0.11.0 joda_time_version=2.10.1 From eb4c31a2a1faf0439ae48637726595dcd9f62609 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 28 Apr 2019 08:57:51 +0800 Subject: [PATCH 08/12] coroutines 1.2.1 --- CHANGELOG.md | 1 + gradle.properties | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04e3fd93c2..8b57f0f66e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ some default library * `AbstractRequestCallFactory` was added with cache of methods urls to avoid memory leaks * Small refactoring of work with response in `KtorRequestsExecutor` * Kotlin version `1.3.30` -> `1.3.31` +* Kotlin coroutines `1.2.0` -> `1.2.1` ## 0.13.0 Telegram Polls diff --git a/gradle.properties b/gradle.properties index d341c6ba14..e5002968b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ kotlin.code.style=official kotlin_version=1.3.31 -kotlin_coroutines_version=1.2.0 +kotlin_coroutines_version=1.2.1 kotlin_serialisation_runtime_version=0.11.0 joda_time_version=2.10.1 ktor_version=1.1.4 From 7977071d1ae21269034071308be46317b9fa38f8 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 30 Apr 2019 12:41:52 +0800 Subject: [PATCH 09/12] adapt raw message for forwarded messages --- .../TelegramBotAPI/types/message/RawMessage.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt index 8fc84d4e5d..b9220fdadd 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt @@ -134,8 +134,8 @@ data class RawMessage( @Transient private val forwarded: ForwardedMessage? by lazy { forward_date ?: return@lazy null // According to the documentation, now any forwarded message contains this field - forward_from_message_id ?.let { - forward_from ?: throw IllegalStateException("For common forwarded messages author of original message declared as set up required") + forward_from ?.let { + forward_from_message_id ?: throw IllegalStateException("For common forwarded messages message id of the original message declared as set up required") forward_from_chat ?.let { ForwardedFromChannelMessage( forward_from_message_id, From 562bc0357d56be1b285361419c0ab972faf61437 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 30 Apr 2019 12:53:56 +0800 Subject: [PATCH 10/12] one more fix for forwarded messages --- .../TelegramBotAPI/types/message/RawMessage.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt index b9220fdadd..2bfaea70b5 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt @@ -134,8 +134,12 @@ data class RawMessage( @Transient private val forwarded: ForwardedMessage? by lazy { forward_date ?: return@lazy null // According to the documentation, now any forwarded message contains this field - forward_from ?.let { - forward_from_message_id ?: throw IllegalStateException("For common forwarded messages message id of the original message declared as set up required") + forward_sender_name ?.let { + AnonymousForwardedMessage( + forward_date, + forward_sender_name + ) + } ?: forward_from_message_id ?.let { forward_from_chat ?.let { ForwardedFromChannelMessage( forward_from_message_id, @@ -147,12 +151,7 @@ data class RawMessage( } ?: CommonForwardedMessage( forward_from_message_id, forward_date, - forward_from - ) - } ?: forward_sender_name ?.let { - AnonymousForwardedMessage( - forward_date, - forward_sender_name + forward_from ?: throw IllegalStateException("For common forwarded messages author of original message declared as set up required") ) } } From cfb988ec7fbebb69edb824cc7c6371dc919b5a0b Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 30 Apr 2019 15:56:28 +0800 Subject: [PATCH 11/12] forwarded messages changes --- CHANGELOG.md | 6 +++++ .../types/message/ForwardedMessage.kt | 23 ++++++++--------- .../types/message/RawMessage.kt | 25 ++++++++----------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b57f0f66e..52759d01f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ some default library * Small refactoring of work with response in `KtorRequestsExecutor` * Kotlin version `1.3.30` -> `1.3.31` * Kotlin coroutines `1.2.0` -> `1.2.1` +* `CommonForwardedMessage` was renamed to `UserForwardedMessage` +* All forwarded messages are now just childs of `ForwardedMessage`: + * `AnonymousForwardedMessage` - for messages without forwarded info + * `UserForwardedMessage` - for messages from users and groups (contains not message id) + * `ForwardedFromChannelMessage` - for messages from channels +* Changed logic of forwarded messages preparing ## 0.13.0 Telegram Polls diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/ForwardedMessage.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/ForwardedMessage.kt index 8c991da6b7..dc970597bc 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/ForwardedMessage.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/ForwardedMessage.kt @@ -12,21 +12,20 @@ data class AnonymousForwardedMessage( val senderName: String ) : ForwardedMessage() -sealed class PublicForwardedMessage : ForwardedMessage() { - abstract val messageId: MessageIdentifier - abstract val from: User? -} - -data class CommonForwardedMessage( - override val messageId: MessageIdentifier, +data class UserForwardedMessage( override val dateOfOriginal: TelegramDate, - override val from: User -) : PublicForwardedMessage() + val from: User +) : ForwardedMessage() + +@Deprecated( + "Renamed according to correct meaning", + ReplaceWith("UserForwardedMessage", "com.github.insanusmokrassar.TelegramBotAPI.types.message.UserForwardedMessage") +) +typealias CommonForwardedMessage = UserForwardedMessage data class ForwardedFromChannelMessage( - override val messageId: MessageIdentifier, override val dateOfOriginal: TelegramDate, - override val from: User?, + val messageId: MessageIdentifier, val channelChat: Chat, val signature: String? = null -) : PublicForwardedMessage() +) : ForwardedMessage() diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt index 2bfaea70b5..b30b4dc58b 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/message/RawMessage.kt @@ -134,25 +134,22 @@ data class RawMessage( @Transient private val forwarded: ForwardedMessage? by lazy { forward_date ?: return@lazy null // According to the documentation, now any forwarded message contains this field - forward_sender_name ?.let { - AnonymousForwardedMessage( + when { + forward_sender_name != null -> AnonymousForwardedMessage( forward_date, forward_sender_name ) - } ?: forward_from_message_id ?.let { - forward_from_chat ?.let { - ForwardedFromChannelMessage( - forward_from_message_id, - forward_date, - forward_from, - forward_from_chat.extractChat(), - forward_signature - ) - } ?: CommonForwardedMessage( - forward_from_message_id, + forward_from_chat != null -> ForwardedFromChannelMessage( forward_date, - forward_from ?: throw IllegalStateException("For common forwarded messages author of original message declared as set up required") + forward_from_message_id ?: throw IllegalStateException("Channel forwarded message must contain message id, but was not"), + forward_from_chat.extractChat(), + forward_signature ) + forward_from != null -> UserForwardedMessage( + forward_date, + forward_from + ) + else -> null } } From 4c70262fe6b942a12a888159c18d1fe429fbc480 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 5 May 2019 09:04:48 +0800 Subject: [PATCH 12/12] update README --- README.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 37b4ed1e7a..b11863474c 100644 --- a/README.md +++ b/README.md @@ -46,23 +46,82 @@ compile "com.github.insanusmokrassar:TelegramBotAPI:${telegrambotapi.version}" ## How to work with library? -By default in any documentation will be meaning that you have variable in scope with names +For now, this library have no some API god-object. Instead of this, this library has several +important objects: -| Name of variable | Description | Where to get? (Examples) | -|:----------------:|:-----------:|:------------------------:| -| executor | [RequestsExecutor](src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/RequestsExecutor.kt) | [Ktor RequestExecutor realisation](src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt) | +* [RequestsExecutor](src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/RequestsExecutor.kt) +* [Requests](src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests) +* [Types](src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types) -## Requests Examples +### Types -### Get Me +Types declare different objects representation. For example, `Chat` for now represented as +interface and has several realisations: + +* PrivateChat +* GroupChat +* SupergroupChat +* ChannelChat + +Instead of common garbage with all information as in original [Chat](https://core.telegram.org/bots/api#chat), +here it was separated for more obvious difference between chats types and their possible content. + +The same principle work with a lot of others things in this Telegram bot API. + +### Requests + +Requests usually are very simple objects, but some of them are using their own +build factories. For example, the next code show, how to get information about bot: ```kotlin -executor.execute(GetMe()) +val requestsExecutor: RequestsExecutor = ... +requestsExecutor.execute(GetMe()) +``` + +The result type of [GetMe](src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/GetMe) request is +[User](src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/User). In fact, in this result must contain +`isBot` equal to `true` always. + + +### RequestsExecutor + +It is base object which can be used to execute requests in API. For now by default included Ktor +realisation of `RequestsExecutor`, but it is possible, that in future it will be extracted in separated +project. How to create `RequestsExecutor`: + +```kotlin +val requestsExecutor = KtorRequestsExecutor(TOKEN) ``` -As a result you will receive `User` object. This object used as is now (as in API documentation), but it is possible -that this class will be renamed to `RawUser` and you will be able to get real realisation of this object like `Bot` (in -cases when `isBot` == `true`) or `User` (otherwise) +Here `KtorRequestsExecutor` - default realisation with Ktor. `TOKEN` is just a token of bot which was retrieved +according to [instruction](https://core.telegram.org/bots#3-how-do-i-create-a-bot). + +Besides, for correct usage of this, you must implement in your project both one of engines for client and server +Ktor libraries: + +```groovy +dependencies { + // ... + implementation "io.ktor:ktor-server-cio:$ktor_version" + implementation "io.ktor:ktor-client-okhttp:$ktor_version" + // ... +} +``` + +It is able to avoid using of `server` dependency in case if will not be used `Webhook`s. In this case, +dependencies list will be simplify: + +```groovy +dependencies { + // ... + implementation "io.ktor:ktor-client-okhttp:$ktor_version" + // ... +} +``` + +Here was used `okhttp` realisation of client, but there are several others engines for Ktor. More information +available on ktor.io site for [client](https://ktor.io/clients/http-client/engines.html) and [server](https://ktor.io/quickstart/artifacts.html) +engines. ## Getting updates @@ -95,3 +154,22 @@ Template for Nginx server config you can find in [this gist](https://gist.github For webhook you can provide `File` with public part of certificate, `URL` where bot will be available and inner `PORT` which will be used to start receiving of updates. Actually, you can skip passing of `File` when you have something like nginx for proxy forwarding. + +In case of using `nginx` with reverse-proxy config, setting up of Webhook will look like: + +```kotlin +requestsExecutor.setWebhook( + WEBHOOK_URL, + INTERNAL_PORT, + filter, + ENGINE_FACTORY +) +``` + +Here: + +* `WEBHOOK_URL` - the url which will be used by Telegram system to send updates +* `INTERNAL_PORT` - the port which will be used in bot for listening of updates +* `filter` - instance of [UpdatesFilter](src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/UpdatesFilter.kt), +which will be used to filter incoming updates +* `ENGINE_FACTORY` - used factory name, for example, `CIO` in case of usage `io.ktor:ktor-server-cio` as server engine