1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-06-26 11:38:09 +00:00

Merge pull request #81 from InsanusMokrassar/0.26.3

0.26.3
This commit is contained in:
InsanusMokrassar 2020-04-13 12:55:48 +06:00 committed by GitHub
commit f1480c40a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 188 additions and 115 deletions

View File

@ -38,6 +38,22 @@
and size of retrieved updates is equal to 100 (max count of retrieved updates)
* Extensions `getUpdates` now will receive only not nullable `limit` parameter
### 0.26.3
* `TelegramBotAPI`:
* `CallbackGameInlineKeyboardButton` was added
([Issue-79](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/79),
[PR-80](https://github.com/InsanusMokrassar/TelegramBotAPI/pull/80))
* `UnknownInlineKeyboardButton` was added. It is unavailable for creating, but you can receive it, for example, in
`InlineQueryResult`
* `Update` now will be created even if was `SerializationException` inside of creating the update instance - in this
case will be created `UnknownUpdateType`
* `UnknownUpdateType$rawJson` value now is included (`JsonElement`)
* **EXPERIMENTALLY** `BaseEditMessageUpdate#data` now is `CommonMessage<*>`
* Suspend inline function `handleSafely` was added
* `KtorRequestsExecutor` now use `handleSafely` instead of `try` with `supervisorScope`
* `UpdatesPolling` now use `handleSafely` instead of `try` with `supervisorScope`
### 0.26.2
* `TelegramBotAPI`:

View File

@ -11,6 +11,7 @@ import com.github.insanusmokrassar.TelegramBotAPI.types.update.MediaGroupUpdates
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.Update
import com.github.insanusmokrassar.TelegramBotAPI.updateshandlers.*
import com.github.insanusmokrassar.TelegramBotAPI.utils.PreviewFeature
import com.github.insanusmokrassar.TelegramBotAPI.utils.handleSafely
import io.ktor.client.features.HttpRequestTimeoutException
import kotlinx.coroutines.*
@ -24,42 +25,40 @@ fun RequestsExecutor.startGettingOfUpdates(
var lastUpdateIdentifier: UpdateIdentifier? = null
while (isActive) {
try {
supervisorScope {
val updates = getUpdates(
offset = lastUpdateIdentifier?.plus(1),
timeout = timeoutSeconds,
allowed_updates = allowedUpdates
).let { originalUpdates ->
val converted = originalUpdates.convertWithMediaGroupUpdates()
/**
* Dirty hack for cases when the media group was retrieved not fully:
*
* We are throw out the last media group and will reretrieve it again in the next get updates
* and it will guarantee that it is full
*/
if (originalUpdates.size == getUpdatesLimit.last && converted.last() is SentMediaGroupUpdate) {
converted - converted.last()
} else {
converted
}
}
supervisorScope {
for (update in updates) {
updatesReceiver(update)
lastUpdateIdentifier = update.lastUpdateIdentifier()
}
handleSafely(
{ e ->
exceptionsHandler ?.invoke(e)
if (e is RequestException) {
delay(1000L)
}
}
) {
val updates = getUpdates(
offset = lastUpdateIdentifier?.plus(1),
timeout = timeoutSeconds,
allowed_updates = allowedUpdates
).let { originalUpdates ->
val converted = originalUpdates.convertWithMediaGroupUpdates()
/**
* Dirty hack for cases when the media group was retrieved not fully:
*
* We are throw out the last media group and will reretrieve it again in the next get updates
* and it will guarantee that it is full
*/
if (originalUpdates.size == getUpdatesLimit.last && converted.last() is SentMediaGroupUpdate) {
converted - converted.last()
} else {
converted
}
}
handleSafely {
for (update in updates) {
updatesReceiver(update)
lastUpdateIdentifier = update.lastUpdateIdentifier()
}
}
} catch (e: HttpRequestTimeoutException) {
exceptionsHandler ?.invoke(e) // it is ok due to mechanism of long polling
} catch (e: RequestException) {
exceptionsHandler ?.invoke(e) // it is not ok, but in most cases it will mean that there is some limit for requests count
delay(1000L)
} catch (e: Exception) {
exceptionsHandler ?.invoke(e)
}
}
}

View File

@ -9,7 +9,7 @@ import com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters.RequestL
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
import com.github.insanusmokrassar.TelegramBotAPI.types.Response
import com.github.insanusmokrassar.TelegramBotAPI.types.RetryAfterError
import com.github.insanusmokrassar.TelegramBotAPI.utils.TelegramAPIUrlsKeeper
import com.github.insanusmokrassar.TelegramBotAPI.utils.*
import com.github.insanusmokrassar.TelegramBotAPI.utils.nonstrictJsonFormat
import io.ktor.client.HttpClient
import io.ktor.client.call.receive
@ -17,7 +17,6 @@ import io.ktor.client.features.*
import io.ktor.client.statement.HttpStatement
import io.ktor.client.statement.readText
import kotlinx.coroutines.delay
import kotlinx.coroutines.supervisorScope
import kotlinx.serialization.json.Json
class KtorRequestsExecutor(
@ -43,54 +42,56 @@ class KtorRequestsExecutor(
}
override suspend fun <T : Any> execute(request: Request<T>): T {
return try {
supervisorScope {
requestsLimiter.limit {
var statement: HttpStatement? = null
for (factory in callsFactories) {
statement = factory.prepareCall(
client,
telegramAPIUrlsKeeper.commonAPIUrl,
request
)
if (statement != null) {
break
}
}
val response = statement?.execute() ?: throw IllegalArgumentException("Can't execute request: $request")
val content = response.receive<String>()
return handleSafely(
{ e ->
throw if (e is ClientRequestException) {
val content = e.response.readText()
val responseObject = jsonFormatter.parse(Response.serializer(), content)
(responseObject.result?.let {
jsonFormatter.fromJson(request.resultDeserializer, it)
} ?: responseObject.parameters?.let {
val error = it.error
if (error is RetryAfterError) {
delay(error.leftToRetry)
execute(request)
} else {
null
}
} ?: response.let {
throw newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
})
newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
} else {
e
}
}
} catch (e: ClientRequestException) {
val content = e.response.readText()
val responseObject = jsonFormatter.parse(Response.serializer(), content)
throw newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
} catch (e: Exception) {
throw e
) {
requestsLimiter.limit {
var statement: HttpStatement? = null
for (factory in callsFactories) {
statement = factory.prepareCall(
client,
telegramAPIUrlsKeeper.commonAPIUrl,
request
)
if (statement != null) {
break
}
}
val response = statement?.execute() ?: throw IllegalArgumentException("Can't execute request: $request")
val content = response.receive<String>()
val responseObject = jsonFormatter.parse(Response.serializer(), content)
(responseObject.result?.let {
jsonFormatter.fromJson(request.resultDeserializer, it)
} ?: responseObject.parameters?.let {
val error = it.error
if (error is RetryAfterError) {
delay(error.leftToRetry)
execute(request)
} else {
null
}
} ?: response.let {
throw newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
})
}
}
}

View File

@ -82,6 +82,7 @@ const val containsMasksField = "contains_masks"
const val resultIdField = "result_id"
const val inlineMessageIdField = "inline_message_id"
const val callbackDataField = "callback_data"
const val callbackGameField = "callback_game"
const val callbackQueryIdField = "callback_query_id"
const val inlineQueryIdField = "inline_query_id"
const val inlineKeyboardField = "inline_keyboard"

View File

@ -1,14 +1,23 @@
package com.github.insanusmokrassar.TelegramBotAPI.types.buttons.InlineKeyboardButtons
import com.github.insanusmokrassar.TelegramBotAPI.types.*
import com.github.insanusmokrassar.TelegramBotAPI.types.games.CallbackGame
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
@Serializable(InlineKeyboardButtonSerializer::class)
sealed class InlineKeyboardButton {
abstract val text: String
}
@Serializable
data class UnknownInlineKeyboardButton internal constructor(
override val text: String,
val rawData: JsonElement
) : InlineKeyboardButton()
@Serializable
data class PayInlineKeyboardButton(
override val text: String,
@ -24,6 +33,14 @@ data class CallbackDataInlineKeyboardButton(
val callbackData: String
) : InlineKeyboardButton()
@Serializable
data class CallbackGameInlineKeyboardButton(
@SerialName(textField)
override val text: String,
@SerialName(callbackGameField)
val callbackGame: CallbackGame
) : InlineKeyboardButton()
@Serializable
data class LoginURLInlineKeyboardButton(
override val text: String,

View File

@ -3,8 +3,7 @@ package com.github.insanusmokrassar.TelegramBotAPI.types.buttons.InlineKeyboardB
import com.github.insanusmokrassar.TelegramBotAPI.types.*
import com.github.insanusmokrassar.TelegramBotAPI.utils.nonstrictJsonFormat
import kotlinx.serialization.*
import kotlinx.serialization.json.JsonElementSerializer
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.*
internal object InlineKeyboardButtonSerializer : KSerializer<InlineKeyboardButton> {
override val descriptor: SerialDescriptor = SerialDescriptor(
@ -12,22 +11,25 @@ internal object InlineKeyboardButtonSerializer : KSerializer<InlineKeyboardButto
PolymorphicKind.SEALED
)
private fun resolveSerializer(json: JsonObject): KSerializer<out InlineKeyboardButton> {
private fun resolveSerializer(json: JsonObject): KSerializer<out InlineKeyboardButton>? {
return when {
json[callbackDataField] != null -> CallbackDataInlineKeyboardButton.serializer()
json[callbackGameField] != null -> CallbackGameInlineKeyboardButton.serializer()
json[loginUrlField] != null -> LoginURLInlineKeyboardButton.serializer()
json[payField] != null -> PayInlineKeyboardButton.serializer()
json[switchInlineQueryField] != null -> SwitchInlineQueryInlineKeyboardButton.serializer()
json[switchInlineQueryCurrentChatField] != null -> SwitchInlineQueryCurrentChatInlineKeyboardButton.serializer()
json[urlField] != null -> URLInlineKeyboardButton.serializer()
else -> throw IllegalArgumentException("Can't find correct serializer for inline button serialized as $json")
else -> null
}
}
override fun deserialize(decoder: Decoder): InlineKeyboardButton {
val json = JsonElementSerializer.deserialize(decoder)
return nonstrictJsonFormat.fromJson(resolveSerializer(json.jsonObject), json)
return (json as? JsonObject) ?.let { resolveSerializer(it) } ?.let {
nonstrictJsonFormat.fromJson(it, json)
} ?: UnknownInlineKeyboardButton("", json)
}
override fun serialize(encoder: Encoder, value: InlineKeyboardButton) {
@ -38,6 +40,8 @@ internal object InlineKeyboardButtonSerializer : KSerializer<InlineKeyboardButto
is SwitchInlineQueryInlineKeyboardButton -> SwitchInlineQueryInlineKeyboardButton.serializer().serialize(encoder, value)
is SwitchInlineQueryCurrentChatInlineKeyboardButton -> SwitchInlineQueryCurrentChatInlineKeyboardButton.serializer().serialize(encoder, value)
is URLInlineKeyboardButton -> URLInlineKeyboardButton.serializer().serialize(encoder, value)
is CallbackGameInlineKeyboardButton -> CallbackGameInlineKeyboardButton.serializer().serialize(encoder, value)
is UnknownInlineKeyboardButton -> JsonElementSerializer.serialize(encoder, value.rawData)
}
}
}

View File

@ -1,10 +1,10 @@
package com.github.insanusmokrassar.TelegramBotAPI.types.update
import com.github.insanusmokrassar.TelegramBotAPI.types.UpdateIdentifier
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.Message
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.CommonMessage
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.BaseEditMessageUpdate
data class EditChannelPostUpdate(
override val updateId: UpdateIdentifier,
override val data: Message
override val data: CommonMessage<*>
) : BaseEditMessageUpdate

View File

@ -1,10 +1,10 @@
package com.github.insanusmokrassar.TelegramBotAPI.types.update
import com.github.insanusmokrassar.TelegramBotAPI.types.UpdateIdentifier
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.Message
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.CommonMessage
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.BaseEditMessageUpdate
data class EditMessageUpdate(
override val updateId: UpdateIdentifier,
override val data: Message
override val data: CommonMessage<*>
) : BaseEditMessageUpdate

View File

@ -4,7 +4,7 @@ import com.github.insanusmokrassar.TelegramBotAPI.types.CallbackQuery.RawCallbac
import com.github.insanusmokrassar.TelegramBotAPI.types.InlineQueries.ChosenInlineResult.RawChosenInlineResult
import com.github.insanusmokrassar.TelegramBotAPI.types.InlineQueries.query.RawInlineQuery
import com.github.insanusmokrassar.TelegramBotAPI.types.UpdateIdentifier
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.Message
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.*
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializer
import com.github.insanusmokrassar.TelegramBotAPI.types.payments.PreCheckoutQuery
import com.github.insanusmokrassar.TelegramBotAPI.types.payments.ShippingQuery
@ -13,8 +13,7 @@ import com.github.insanusmokrassar.TelegramBotAPI.types.polls.PollAnswer
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.UnknownUpdateType
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.Update
import com.github.insanusmokrassar.TelegramBotAPI.types.updateIdField
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.*
import kotlinx.serialization.json.JsonElement
@Serializable
@ -22,11 +21,11 @@ internal data class RawUpdate constructor(
@SerialName(updateIdField)
val updateId: UpdateIdentifier,
@Serializable(TelegramBotAPIMessageDeserializeOnlySerializer::class)
private val edited_message: Message? = null,
private val edited_message: CommonMessage<*>? = null,
@Serializable(TelegramBotAPIMessageDeserializeOnlySerializer::class)
private val message: Message? = null,
@Serializable(TelegramBotAPIMessageDeserializeOnlySerializer::class)
private val edited_channel_post: Message? = null,
private val edited_channel_post: CommonMessage<*>? = null,
@Serializable(TelegramBotAPIMessageDeserializeOnlySerializer::class)
private val channel_post: Message? = null,
private val inline_query: RawInlineQuery? = null,
@ -42,25 +41,34 @@ internal data class RawUpdate constructor(
* @return One of children of [Update] interface or null in case of unknown type of update
*/
fun asUpdate(raw: JsonElement): Update {
return initedUpdate ?: when {
edited_message != null -> EditMessageUpdate(updateId, edited_message)
message != null -> MessageUpdate(updateId, message)
edited_channel_post != null -> EditChannelPostUpdate(updateId, edited_channel_post)
channel_post != null -> ChannelPostUpdate(updateId, channel_post)
return initedUpdate ?: try {
when {
edited_message != null -> EditMessageUpdate(updateId, edited_message)
message != null -> MessageUpdate(updateId, message)
edited_channel_post != null -> EditChannelPostUpdate(updateId, edited_channel_post)
channel_post != null -> ChannelPostUpdate(updateId, channel_post)
chosen_inline_result != null -> ChosenInlineResultUpdate(updateId, chosen_inline_result.asChosenInlineResult)
inline_query != null -> InlineQueryUpdate(updateId, inline_query.asInlineQuery)
callback_query != null -> CallbackQueryUpdate(
chosen_inline_result != null -> ChosenInlineResultUpdate(updateId, chosen_inline_result.asChosenInlineResult)
inline_query != null -> InlineQueryUpdate(updateId, inline_query.asInlineQuery)
callback_query != null -> CallbackQueryUpdate(
updateId,
callback_query.asCallbackQuery(raw.jsonObject["callback_query"].toString())
)
shipping_query != null -> ShippingQueryUpdate(updateId, shipping_query)
pre_checkout_query != null -> PreCheckoutQueryUpdate(updateId, pre_checkout_query)
poll != null -> PollUpdate(updateId, poll)
poll_answer != null -> PollAnswerUpdate(updateId, poll_answer)
else -> UnknownUpdateType(
updateId,
raw.toString(),
raw
)
}
} catch (e: SerializationException) {
UnknownUpdateType(
updateId,
callback_query.asCallbackQuery(raw.jsonObject["callback_query"].toString())
)
shipping_query != null -> ShippingQueryUpdate(updateId, shipping_query)
pre_checkout_query != null -> PreCheckoutQueryUpdate(updateId, pre_checkout_query)
poll != null -> PollUpdate(updateId, poll)
poll_answer != null -> PollAnswerUpdate(updateId, poll_answer)
else -> UnknownUpdateType(
updateId,
raw.toString()
raw.toString(),
raw
)
}.also {
initedUpdate = it

View File

@ -1,3 +1,7 @@
package com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts
interface BaseEditMessageUpdate : BaseMessageUpdate
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.CommonMessage
interface BaseEditMessageUpdate : BaseMessageUpdate {
override val data: CommonMessage<*>
}

View File

@ -4,6 +4,7 @@ import com.github.insanusmokrassar.TelegramBotAPI.types.UpdateIdentifier
import com.github.insanusmokrassar.TelegramBotAPI.types.update.RawUpdate
import com.github.insanusmokrassar.TelegramBotAPI.utils.nonstrictJsonFormat
import kotlinx.serialization.*
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonElementSerializer
interface Update {
@ -13,7 +14,8 @@ interface Update {
data class UnknownUpdateType(
override val updateId: UpdateIdentifier,
override val data: String
override val data: String,
val rawJson: JsonElement
) : Update
internal object UpdateSerializerWithoutDeserialization : KSerializer<Update> {

View File

@ -0,0 +1,21 @@
package com.github.insanusmokrassar.TelegramBotAPI.utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.supervisorScope
/**
* It will run [block] inside of [supervisorScope] to avoid problems with catching of exceptions
*
* @param [onException] Will be called when happen exception inside of [block]. By default will throw exception - this
* exception will be available for catching
*/
suspend inline fun <T> handleSafely(
noinline onException: suspend (Exception) -> T = { throw it },
noinline block: suspend CoroutineScope.() -> T
): T {
return try {
supervisorScope(block)
} catch (e: Exception) {
onException(e)
}
}

View File

@ -7,6 +7,6 @@ uuid_version=0.1.0
ktor_version=1.3.2
library_group=com.github.insanusmokrassar
library_version=0.26.2
library_version=0.26.3
gradle_bintray_plugin_version=1.8.4