diff --git a/CHANGELOG.md b/CHANGELOG.md index 01928c9549..6a0e26ee31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ * Ktor `1.3.0` -> `1.3.1` * Now it is possible to get updates by polling with custom executor engine * `CommonMultipartFileRequest` now is internal +* Added `LiveLocation` class for more useful tracking live locations ## 0.22.0 diff --git a/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/LiveLocation.kt b/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/LiveLocation.kt new file mode 100644 index 0000000000..a769e86bc1 --- /dev/null +++ b/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/LiveLocation.kt @@ -0,0 +1,110 @@ +package com.github.insanusmokrassar.TelegramBotAPI.requests + +import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestsExecutor +import com.github.insanusmokrassar.TelegramBotAPI.requests.edit.LiveLocation.EditChatMessageLiveLocation +import com.github.insanusmokrassar.TelegramBotAPI.requests.edit.LiveLocation.StopChatMessageLiveLocation +import com.github.insanusmokrassar.TelegramBotAPI.requests.send.SendLocation +import com.github.insanusmokrassar.TelegramBotAPI.types.* +import com.github.insanusmokrassar.TelegramBotAPI.types.buttons.InlineKeyboardMarkup +import com.github.insanusmokrassar.TelegramBotAPI.types.buttons.KeyboardMarkup +import com.soywiz.klock.DateTime +import com.soywiz.klock.TimeSpan +import io.ktor.utils.io.core.Closeable +import kotlinx.coroutines.* + +private val livePeriodDelayDouble = ((livePeriodLimit.last - 60L) * 1000L).toDouble() +class LiveLocation internal constructor( + private val scope: CoroutineScope, + private val requestsExecutor: RequestsExecutor, + private val chatId: ChatIdentifier, + private val messageId: MessageIdentifier, + location: Location +) : Closeable { + var isClosed: Boolean = false + private set + private var autoCloseTime = DateTime.now() + TimeSpan(livePeriodDelayDouble) + val leftUntilCloseMillis: TimeSpan + get() = autoCloseTime - DateTime.now() + private var updateJob: Job? = null + var lastLocation: Location = location + private set(value) { + field = value + updateJob ?.cancel() + updateJob = scope.launch { + autoCloseTime = DateTime.now() + TimeSpan(livePeriodDelayDouble) + delay(leftUntilCloseMillis.millisecondsLong) + updateJob = null + close() + } + } + + init { + this.lastLocation = location // required to init updateJob + } + + suspend fun updateLocation( + location: Location, + replyMarkup: InlineKeyboardMarkup? = null + ): Location { + if (!isClosed) { + lastLocation = requestsExecutor.execute( + EditChatMessageLiveLocation( + chatId, + messageId, + location.latitude, + location.longitude, + replyMarkup + ) + ).content.location + return lastLocation + } else { + error("LiveLocation is closed") + } + } + + override fun close() { + if (isClosed) { + return + } + isClosed = true + updateJob ?.cancel() + scope.launch { + requestsExecutor.execute( + StopChatMessageLiveLocation( + chatId, + messageId + ) + ) + } + } +} + +suspend fun RequestsExecutor.startLiveLocation( + scope: CoroutineScope, + chatId: ChatIdentifier, + latitude: Double, + longitude: Double, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null, + replyMarkup: KeyboardMarkup? = null +): LiveLocation { + val locationMessage = execute( + SendLocation( + chatId, + latitude, + longitude, + livePeriodLimit.last.toLong(), + disableNotification, + replyToMessageId, + replyMarkup + ) + ) + + return LiveLocation( + scope, + this, + chatId, + locationMessage.messageId, + locationMessage.content.location + ) +} diff --git a/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/send/SendLocation.kt b/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/send/SendLocation.kt index 67ff01360f..f7e7d42c56 100644 --- a/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/send/SendLocation.kt +++ b/src/commonMain/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/send/SendLocation.kt @@ -1,11 +1,20 @@ package com.github.insanusmokrassar.TelegramBotAPI.requests.send +import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestsExecutor +import com.github.insanusmokrassar.TelegramBotAPI.requests.edit.LiveLocation.EditChatMessageLiveLocation +import com.github.insanusmokrassar.TelegramBotAPI.requests.edit.LiveLocation.StopChatMessageLiveLocation import com.github.insanusmokrassar.TelegramBotAPI.requests.send.abstracts.* import com.github.insanusmokrassar.TelegramBotAPI.types.* +import com.github.insanusmokrassar.TelegramBotAPI.types.buttons.InlineKeyboardMarkup import com.github.insanusmokrassar.TelegramBotAPI.types.buttons.KeyboardMarkup import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.ContentMessage import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.TelegramBotAPIMessageDeserializationStrategyClass import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.LocationContent +import com.github.insanusmokrassar.TelegramBotAPI.utils.extensions.executeAsync +import com.soywiz.klock.DateTime +import com.soywiz.klock.TimeSpan +import io.ktor.utils.io.core.Closeable +import kotlinx.coroutines.* import kotlinx.serialization.* @@ -33,10 +42,48 @@ data class SendLocation( ReplyingMarkupSendMessageRequest>, PositionedSendMessageRequest> { - override fun method(): String = "sendLocation" override val resultDeserializer: DeserializationStrategy> get() = commonResultDeserializer override val requestSerializer: SerializationStrategy<*> get() = serializer() + + init { + if (livePeriod != null && livePeriod !in livePeriodLimit) { + error("Live period for sending location must be in $livePeriodLimit") + } + } } + +suspend fun RequestsExecutor.sendLocation( + chatId: ChatIdentifier, + latitude: Double, + longitude: Double, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null, + replyMarkup: KeyboardMarkup? = null +) = execute( + SendLocation( + chatId, + latitude, + longitude, + disableNotification = disableNotification, + replyToMessageId = replyToMessageId, + replyMarkup = replyMarkup + ) +) + +suspend fun RequestsExecutor.sendLocation( + chatId: ChatIdentifier, + location: Location, + disableNotification: Boolean = false, + replyToMessageId: MessageIdentifier? = null, + replyMarkup: KeyboardMarkup? = null +) = sendLocation( + chatId, + location.latitude, + location.longitude, + disableNotification, + replyToMessageId, + replyMarkup +)