mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2024-11-22 08:13:47 +00:00
commit
2dda103d51
@ -42,3 +42,12 @@
|
|||||||
* Add extension `String#toMarkdown`
|
* Add extension `String#toMarkdown`
|
||||||
* Fix of inserting of text when create Markdown-adapted text from text and text entities
|
* Fix of inserting of text when create Markdown-adapted text from text and text entities
|
||||||
* Fix default realisation of MessageEntity#asMarkdownSource
|
* Fix default realisation of MessageEntity#asMarkdownSource
|
||||||
|
|
||||||
|
### 0.9.0
|
||||||
|
|
||||||
|
* Old extension `OkHttpClient.Builder#useWith` now deprecated and must be replaced by the same in
|
||||||
|
`com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor` package
|
||||||
|
* Replace `ProxySettings` data class in `settings` package, deprecate old link
|
||||||
|
* `BaseRequestsExecutor` now have no it's own scope
|
||||||
|
* Add `RequestLimiter` and base realisations
|
||||||
|
* Now `KtorRequestsExecutor` can receive as one of parameters `RequestLimiter` (by default - `EmptyLimiter`)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
project.version = "0.8.5"
|
project.version = "0.9.0"
|
||||||
project.group = "com.github.insanusmokrassar"
|
project.group = "com.github.insanusmokrassar"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
@ -3,6 +3,6 @@ kotlin_version=1.3.11
|
|||||||
kotlin_coroutines_version=1.1.0
|
kotlin_coroutines_version=1.1.0
|
||||||
kotlin_serialisation_runtime_version=0.9.1
|
kotlin_serialisation_runtime_version=0.9.1
|
||||||
joda_time_version=2.10.1
|
joda_time_version=2.10.1
|
||||||
ktor_version=1.0.1
|
ktor_version=1.1.1
|
||||||
|
|
||||||
gradle_bintray_plugin_version=1.8.4
|
gradle_bintray_plugin_version=1.8.4
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.github.insanusmokrassar.TelegramBotAPI.bot
|
||||||
|
|
||||||
|
abstract class BaseRequestsExecutor(
|
||||||
|
token: String,
|
||||||
|
hostUrl: String = "https://api.telegram.org"
|
||||||
|
) : RequestsExecutor {
|
||||||
|
protected val baseUrl: String = "$hostUrl/bot$token"
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
package com.github.insanusmokrassar.TelegramBotAPI.bot
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
abstract class BaseRequestsExecutor(
|
|
||||||
token: String,
|
|
||||||
hostUrl: String = "https://api.telegram.org"
|
|
||||||
) : RequestsExecutor {
|
|
||||||
protected val baseUrl: String = "$hostUrl/bot$token"
|
|
||||||
|
|
||||||
protected val scope: CoroutineScope = CoroutineScope(
|
|
||||||
Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
|
||||||
)
|
|
||||||
}
|
|
@ -4,6 +4,8 @@ import com.github.insanusmokrassar.TelegramBotAPI.bot.BaseRequestsExecutor
|
|||||||
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base.MultipartRequestCallFactory
|
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base.MultipartRequestCallFactory
|
||||||
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base.SimpleRequestCallFactory
|
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base.SimpleRequestCallFactory
|
||||||
import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestException
|
import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestException
|
||||||
|
import com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters.EmptyLimiter
|
||||||
|
import com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters.RequestLimiter
|
||||||
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
|
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
|
||||||
import com.github.insanusmokrassar.TelegramBotAPI.types.ResponseParameters
|
import com.github.insanusmokrassar.TelegramBotAPI.types.ResponseParameters
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
@ -19,7 +21,8 @@ class KtorRequestsExecutor(
|
|||||||
private val client: HttpClient = HttpClient(OkHttp),
|
private val client: HttpClient = HttpClient(OkHttp),
|
||||||
hostUrl: String = "https://api.telegram.org",
|
hostUrl: String = "https://api.telegram.org",
|
||||||
callsFactories: List<KtorCallFactory> = emptyList(),
|
callsFactories: List<KtorCallFactory> = emptyList(),
|
||||||
excludeDefaultFactories: Boolean = false
|
excludeDefaultFactories: Boolean = false,
|
||||||
|
private val requestsLimiter: RequestLimiter = EmptyLimiter
|
||||||
) : BaseRequestsExecutor(token, hostUrl) {
|
) : BaseRequestsExecutor(token, hostUrl) {
|
||||||
constructor(
|
constructor(
|
||||||
token: String,
|
token: String,
|
||||||
@ -40,30 +43,32 @@ class KtorRequestsExecutor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun <T : Any> execute(request: Request<T>): T {
|
override suspend fun <T : Any> execute(request: Request<T>): T {
|
||||||
var call: HttpClientCall? = null
|
return requestsLimiter.limit {
|
||||||
for (factory in callsFactories) {
|
var call: HttpClientCall? = null
|
||||||
call = factory.prepareCall(
|
for (factory in callsFactories) {
|
||||||
client,
|
call = factory.prepareCall(
|
||||||
baseUrl,
|
client,
|
||||||
request
|
baseUrl,
|
||||||
)
|
request
|
||||||
if (call != null) {
|
)
|
||||||
break
|
if (call != null) {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (call == null) {
|
||||||
if (call == null) {
|
throw IllegalArgumentException("Can't execute request: $request")
|
||||||
throw IllegalArgumentException("Can't execute request: $request")
|
}
|
||||||
}
|
val content = call.response.content.toByteArray().toString(Charset.defaultCharset())
|
||||||
val content = call.response.content.toByteArray().toString(Charset.defaultCharset())
|
val responseObject = JSON.parse(
|
||||||
val responseObject = JSON.parse(
|
ResponseParameters.serializer(request.resultSerializer()),
|
||||||
ResponseParameters.serializer(request.resultSerializer()),
|
content
|
||||||
content
|
|
||||||
)
|
|
||||||
return responseObject.result ?: call.let {
|
|
||||||
throw RequestException(
|
|
||||||
responseObject,
|
|
||||||
"Can't get result object"
|
|
||||||
)
|
)
|
||||||
|
responseObject.result ?: call.let {
|
||||||
|
throw RequestException(
|
||||||
|
responseObject,
|
||||||
|
"Can't get result object"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,37 +1,18 @@
|
|||||||
package com.github.insanusmokrassar.TelegramBotAPI.bot
|
package com.github.insanusmokrassar.TelegramBotAPI.bot
|
||||||
|
|
||||||
import okhttp3.Credentials
|
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.useWith
|
||||||
|
import com.github.insanusmokrassar.TelegramBotAPI.bot.settings.ProxySettings
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import java.net.InetSocketAddress
|
|
||||||
import java.net.Proxy
|
|
||||||
|
|
||||||
data class ProxySettings(
|
@Deprecated(
|
||||||
val host: String = "localhost",
|
"Replaced in settings package",
|
||||||
val port: Int = 1080,
|
ReplaceWith("ProxySettings", "com.github.insanusmokrassar.TelegramBotAPI.bot.settings.ProxySettings")
|
||||||
val username: String? = null,
|
|
||||||
val password: String? = null
|
|
||||||
)
|
)
|
||||||
|
typealias ProxySettings = com.github.insanusmokrassar.TelegramBotAPI.bot.settings.ProxySettings
|
||||||
|
|
||||||
fun OkHttpClient.Builder.useWith(proxySettings: ProxySettings) {
|
|
||||||
proxy(
|
@Deprecated(
|
||||||
Proxy(
|
"Replaced in Ktor package",
|
||||||
Proxy.Type.SOCKS,
|
ReplaceWith("useWith", "com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.useWith")
|
||||||
InetSocketAddress(
|
)
|
||||||
proxySettings.host,
|
fun OkHttpClient.Builder.useWith(proxySettings: ProxySettings) = useWith(proxySettings)
|
||||||
proxySettings.port
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
proxySettings.password ?.let {
|
|
||||||
password ->
|
|
||||||
proxyAuthenticator {
|
|
||||||
_, response ->
|
|
||||||
response.request().newBuilder().apply {
|
|
||||||
addHeader(
|
|
||||||
"Proxy-Authorization",
|
|
||||||
Credentials.basic(proxySettings.username ?: "", password)
|
|
||||||
)
|
|
||||||
}.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.github.insanusmokrassar.TelegramBotAPI.bot.settings
|
||||||
|
|
||||||
|
import kotlinx.serialization.Optional
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ProxySettings(
|
||||||
|
@Optional
|
||||||
|
val host: String = "localhost",
|
||||||
|
@Optional
|
||||||
|
val port: Int = 1080,
|
||||||
|
@Optional
|
||||||
|
val username: String? = null,
|
||||||
|
@Optional
|
||||||
|
val password: String? = null
|
||||||
|
)
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters
|
||||||
|
|
||||||
|
object EmptyLimiter : RequestLimiter {
|
||||||
|
override suspend fun <T> limit(block: suspend () -> T): T = block()
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import kotlin.coroutines.*
|
||||||
|
|
||||||
|
private sealed class RequestEvent
|
||||||
|
private class AddRequest(
|
||||||
|
val continuation: Continuation<Long>
|
||||||
|
) : RequestEvent()
|
||||||
|
private object CompleteRequest : RequestEvent()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PowLimiter(
|
||||||
|
@Optional
|
||||||
|
private val minAwaitTime: Long = 0L,
|
||||||
|
@Optional
|
||||||
|
private val maxAwaitTime: Long = 10000L,
|
||||||
|
@Optional
|
||||||
|
private val powValue: Double = 4.0,
|
||||||
|
@Optional
|
||||||
|
private val powK: Double = 0.0016
|
||||||
|
) : RequestLimiter {
|
||||||
|
@Transient
|
||||||
|
private val scope = CoroutineScope(
|
||||||
|
Executors.newFixedThreadPool(3).asCoroutineDispatcher()
|
||||||
|
)
|
||||||
|
@Transient
|
||||||
|
private val eventsChannel = Channel<RequestEvent>(Channel.UNLIMITED)
|
||||||
|
@Transient
|
||||||
|
private val awaitTimeRange = minAwaitTime .. maxAwaitTime
|
||||||
|
|
||||||
|
init {
|
||||||
|
scope.launch {
|
||||||
|
var requestsInWork: Double = 0.0
|
||||||
|
for (event in eventsChannel) {
|
||||||
|
when (event) {
|
||||||
|
is AddRequest -> {
|
||||||
|
val awaitTime = ((Math.pow(requestsInWork, powValue) * powK) * 1000L).toLong()
|
||||||
|
requestsInWork++
|
||||||
|
|
||||||
|
event.continuation.resume(
|
||||||
|
if (awaitTime in awaitTimeRange) {
|
||||||
|
awaitTime
|
||||||
|
} else {
|
||||||
|
if (awaitTime < minAwaitTime) {
|
||||||
|
minAwaitTime
|
||||||
|
} else {
|
||||||
|
maxAwaitTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is CompleteRequest -> requestsInWork--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun <T> limit(
|
||||||
|
block: suspend () -> T
|
||||||
|
): T {
|
||||||
|
val delayMillis = suspendCoroutine<Long> {
|
||||||
|
scope.launch { eventsChannel.send(AddRequest(it)) }
|
||||||
|
}
|
||||||
|
delay(delayMillis)
|
||||||
|
return try {
|
||||||
|
block()
|
||||||
|
} finally {
|
||||||
|
eventsChannel.send(CompleteRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters
|
||||||
|
|
||||||
|
interface RequestLimiter {
|
||||||
|
/**
|
||||||
|
* Use limit for working of block (like delay between or after, for example)
|
||||||
|
*/
|
||||||
|
suspend fun <T> limit(block: suspend () -> T): T
|
||||||
|
}
|
@ -3,7 +3,9 @@ package com.github.insanusmokrassar.TelegramBotAPI.requests
|
|||||||
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.SimpleRequest
|
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.SimpleRequest
|
||||||
import com.github.insanusmokrassar.TelegramBotAPI.types.User
|
import com.github.insanusmokrassar.TelegramBotAPI.types.User
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
class GetMe : SimpleRequest<User> {
|
class GetMe : SimpleRequest<User> {
|
||||||
override fun method(): String = "getMe"
|
override fun method(): String = "getMe"
|
||||||
override fun resultSerializer(): KSerializer<User> = User.serializer()
|
override fun resultSerializer(): KSerializer<User> = User.serializer()
|
||||||
|
Loading…
Reference in New Issue
Block a user