mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2026-03-05 02:12:40 +00:00
migration
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.MediaGroupIdentifier
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.MediaGroupMessage
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.update.*
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.update.MediaGroupUpdates.*
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.BaseMessageUpdate
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.Update
|
||||
|
||||
private inline val Pair<MediaGroupMessage, *>.message
|
||||
get() = first
|
||||
|
||||
internal fun List<BaseMessageUpdate>.convertWithMediaGroupUpdates(): List<Update> {
|
||||
val resultUpdates = mutableListOf<Update>()
|
||||
val mediaGroups = mutableMapOf<MediaGroupIdentifier, MutableList<BaseMessageUpdate>>()
|
||||
for (update in this) {
|
||||
val asEditMediaGroupMessage = update.toEditMediaGroupUpdate()
|
||||
if (asEditMediaGroupMessage != null) {
|
||||
resultUpdates.add(asEditMediaGroupMessage)
|
||||
} else {
|
||||
val data = update.data
|
||||
if (data is MediaGroupMessage) {
|
||||
(mediaGroups[data.mediaGroupId] ?: mutableListOf<BaseMessageUpdate>().also { mediaGroups[data.mediaGroupId] = it }).add(update)
|
||||
} else {
|
||||
resultUpdates.add(update)
|
||||
}
|
||||
}
|
||||
}
|
||||
mediaGroups.values.map {
|
||||
it.toSentMediaGroupUpdate() ?.let { mediaGroupUpdate ->
|
||||
resultUpdates.add(mediaGroupUpdate)
|
||||
}
|
||||
}
|
||||
return resultUpdates.sortedBy { it.updateId }
|
||||
}
|
||||
|
||||
internal fun List<BaseMessageUpdate>.toSentMediaGroupUpdate(): SentMediaGroupUpdate? = (this as? SentMediaGroupUpdate) ?: let {
|
||||
if (isEmpty()) {
|
||||
return@let null
|
||||
}
|
||||
val resultList = sortedBy { it.updateId }
|
||||
when (first()) {
|
||||
is MessageUpdate -> MessageMediaGroupUpdate(resultList)
|
||||
is ChannelPostUpdate -> ChannelPostMediaGroupUpdate(resultList)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BaseMessageUpdate.toEditMediaGroupUpdate(): EditMediaGroupUpdate? = (this as? EditMediaGroupUpdate) ?: let {
|
||||
when (this) {
|
||||
is EditMessageUpdate -> EditMessageMediaGroupUpdate(this)
|
||||
is EditChannelPostUpdate -> EditChannelPostMediaGroupUpdate(this)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.CommonAbstracts.CaptionedInput
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.MessageEntity.MessageEntity
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.MessageEntity.RegularTextMessageEntity
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.ParseMode.*
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.TextContent
|
||||
|
||||
fun CaptionedInput.fullEntitiesList(): List<MessageEntity> = caption ?.let {
|
||||
convertToFullMessageEntityList(it, captionEntities)
|
||||
} ?: emptyList()
|
||||
|
||||
fun TextContent.fullEntitiesList(): List<MessageEntity> = convertToFullMessageEntityList(text, entities)
|
||||
|
||||
fun convertToFullMessageEntityList(
|
||||
text: String,
|
||||
messageEntities: List<MessageEntity>
|
||||
): List<MessageEntity> {
|
||||
val result = mutableListOf<MessageEntity>()
|
||||
|
||||
var offset = 0
|
||||
for (entity in messageEntities) {
|
||||
val newEntitySize = entity.offset - offset
|
||||
if (newEntitySize > 0) {
|
||||
val regularEntity = RegularTextMessageEntity(offset, newEntitySize, text.substring(offset, entity.offset))
|
||||
result.add(regularEntity)
|
||||
offset += regularEntity.length
|
||||
}
|
||||
result.add(entity)
|
||||
offset += entity.length
|
||||
}
|
||||
val newEntitySize = text.length - offset
|
||||
if (newEntitySize > 0) {
|
||||
result.add(RegularTextMessageEntity(offset, newEntitySize, text.substring(offset, text.length)))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun createFormattedText(
|
||||
entities: List<MessageEntity>,
|
||||
partLength: Int = 4096,
|
||||
mode: ParseMode = MarkdownParseMode
|
||||
): List<String> {
|
||||
val texts = mutableListOf<String>()
|
||||
val textBuilder = StringBuilder(partLength)
|
||||
for (entity in entities) {
|
||||
val string = when (mode) {
|
||||
is MarkdownParseMode -> entity.asMarkdownSource
|
||||
is HTMLParseMode -> entity.asHtmlSource
|
||||
}
|
||||
if (textBuilder.length + string.length > partLength) {
|
||||
if (textBuilder.isNotEmpty()) {
|
||||
texts.add(textBuilder.toString())
|
||||
textBuilder.clear()
|
||||
}
|
||||
val chunked = string.chunked(partLength)
|
||||
val last = chunked.last()
|
||||
textBuilder.append(last)
|
||||
val listToAdd = if (chunked.size > 1) {
|
||||
chunked.subList(0, chunked.size - 1)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
listToAdd.forEach {
|
||||
texts.add(it)
|
||||
}
|
||||
} else {
|
||||
textBuilder.append(string)
|
||||
}
|
||||
}
|
||||
if (textBuilder.isNotEmpty()) {
|
||||
texts.add(textBuilder.toString())
|
||||
textBuilder.clear()
|
||||
}
|
||||
return texts
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.CommonAbstracts.CaptionedInput
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.MessageEntity.MessageEntity
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.ParseMode.HTMLParseMode
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.captionLength
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.TextContent
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.textLength
|
||||
|
||||
fun createHtmlText(
|
||||
entities: List<MessageEntity>,
|
||||
partLength: Int = 4096
|
||||
): List<String> = createFormattedText(entities, partLength, HTMLParseMode)
|
||||
|
||||
fun CaptionedInput.toHtmlCaptions(): List<String> = createHtmlText(
|
||||
fullEntitiesList(),
|
||||
captionLength.endInclusive + 1
|
||||
)
|
||||
|
||||
fun TextContent.toHtmlTexts(): List<String> = createHtmlText(
|
||||
fullEntitiesList(),
|
||||
textLength.endInclusive + 1
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
inline fun <T: Any> T.toJsonWithoutNulls(serializer: SerializationStrategy<T>): JsonObject = toJson(serializer).withoutNulls()
|
||||
|
||||
inline fun <T: Any> T.toJson(serializer: SerializationStrategy<T>): JsonObject = Json.nonstrict.toJson(
|
||||
serializer,
|
||||
this
|
||||
).jsonObject
|
||||
|
||||
fun JsonArray.withoutNulls(): JsonArray {
|
||||
return jsonArray {
|
||||
forEach {
|
||||
when (it) {
|
||||
is JsonObject -> +it.withoutNulls()
|
||||
is JsonArray -> +it.withoutNulls()
|
||||
is JsonPrimitive -> +it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonObject.withoutNulls(): JsonObject {
|
||||
return json {
|
||||
forEach { (k, v) ->
|
||||
when (v) {
|
||||
is JsonObject -> k to v.withoutNulls()
|
||||
is JsonArray -> k to v.withoutNulls()
|
||||
!is JsonNull -> k to v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonObject.mapWithCommonValues(): Map<String, Any> = map {
|
||||
(key, value) ->
|
||||
key to when (value) {
|
||||
is JsonPrimitive -> value.contentOrNull ?: value.booleanOrNull ?: value.doubleOrNull ?: value.floatOrNull ?: value.intOrNull
|
||||
is JsonArray, is JsonObject -> value.toString()
|
||||
is JsonNull -> null
|
||||
}
|
||||
}.toMap().mapNotNullValues()
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
fun <K, V> mapOfNotNull(vararg pairs: Pair<K, V?>): Map<K, V> {
|
||||
return HashMap<K, V>().apply {
|
||||
pairs.forEach {
|
||||
(key, value) ->
|
||||
value ?.also {
|
||||
put(key, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <K, V> Map<K, V?>.mapNotNullValues(): Map<K, V> = asSequence().mapNotNull {
|
||||
it.value ?.let { value ->
|
||||
it.key to value
|
||||
}
|
||||
}.toMap()
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.CommonAbstracts.CaptionedInput
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.MessageEntity.MessageEntity
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.ParseMode.MarkdownParseMode
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.captionLength
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.TextContent
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.textLength
|
||||
|
||||
fun createMarkdownText(
|
||||
entities: List<MessageEntity>,
|
||||
partLength: Int = 4096
|
||||
): List<String> = createFormattedText(entities, partLength, MarkdownParseMode)
|
||||
|
||||
fun CaptionedInput.toMarkdownCaptions(): List<String> = createMarkdownText(
|
||||
fullEntitiesList(),
|
||||
captionLength.endInclusive + 1
|
||||
)
|
||||
|
||||
fun TextContent.toMarkdownTexts(): List<String> = createMarkdownText(
|
||||
fullEntitiesList(),
|
||||
textLength.endInclusive + 1
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.buttons.Matrix
|
||||
|
||||
fun <T> row(block: RowBuilder<T>.() -> Unit): List<T> {
|
||||
return RowBuilder<T>().also(block).row
|
||||
}
|
||||
|
||||
fun <T> MatrixBuilder<T>.row(block: RowBuilder<T>.() -> Unit) {
|
||||
add(RowBuilder<T>().also(block).row)
|
||||
}
|
||||
|
||||
fun <T> matrix(block: MatrixBuilder<T>.() -> Unit): Matrix<T> {
|
||||
return MatrixBuilder<T>().also(block).matrix
|
||||
}
|
||||
|
||||
class RowBuilder<T> {
|
||||
private val mutRow: MutableList<T> = ArrayList()
|
||||
val row: List<T>
|
||||
get() = mutRow
|
||||
|
||||
fun add(t: T) = mutRow.add(t)
|
||||
}
|
||||
|
||||
class MatrixBuilder<T> {
|
||||
private val mutMatrix: MutableList<List<T>> = ArrayList()
|
||||
val matrix: Matrix<T>
|
||||
get() = mutMatrix
|
||||
|
||||
fun add(t: List<T>) = mutMatrix.add(t)
|
||||
operator fun plus(t: List<T>) = add(t)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.MediaGroupIdentifier
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.chat.abstracts.Chat
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.message.ForwardedMessage
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.*
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.BaseMessageUpdate
|
||||
|
||||
val List<BaseMessageUpdate>.forwarded: ForwardedMessage?
|
||||
get() = first().let {
|
||||
(it as? AbleToBeForwardedMessage) ?.forwarded
|
||||
}
|
||||
|
||||
val List<BaseMessageUpdate>.replyTo: Message?
|
||||
get() = first().let {
|
||||
(it as? AbleToReplyMessage) ?.replyTo
|
||||
}
|
||||
|
||||
val List<BaseMessageUpdate>.chat: Chat?
|
||||
get() = first().data.chat
|
||||
|
||||
val List<BaseMessageUpdate>.mediaGroupId: MediaGroupIdentifier?
|
||||
get() = (first().data as? MediaGroupMessage) ?.mediaGroupId
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
expect class StorageFile {
|
||||
val contentType: String
|
||||
val fileName: String
|
||||
fun generateCustomName(): String
|
||||
fun asInput(): Input
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.*
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.ParseMode.*
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.utils.extensions.toHtml
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.utils.extensions.toMarkdown
|
||||
|
||||
const val markdownBoldControl = "*"
|
||||
const val markdownItalicControl = "_"
|
||||
const val markdownCodeControl = "`"
|
||||
const val markdownPreControl = "```"
|
||||
|
||||
const val htmlBoldControl = "b"
|
||||
const val htmlItalicControl = "i"
|
||||
const val htmlCodeControl = "code"
|
||||
const val htmlPreControl = "pre"
|
||||
|
||||
private infix fun String.markdownDefault(controlSymbol: String) = "$controlSymbol$this$controlSymbol"
|
||||
private infix fun String.htmlDefault(controlSymbol: String) = "<$controlSymbol>$this</$controlSymbol>"
|
||||
|
||||
infix fun String.linkMarkdown(link: String): String = "[$this]($link)"
|
||||
infix fun String.linkHTML(link: String): String = "<a href=\"$link\">$this</a>"
|
||||
|
||||
|
||||
fun String.boldMarkdown(): String = markdownDefault(markdownBoldControl)
|
||||
fun String.boldHTML(): String = htmlDefault(htmlBoldControl)
|
||||
|
||||
|
||||
fun String.italicMarkdown(): String = markdownDefault(markdownItalicControl)
|
||||
fun String.italicHTML(): String =htmlDefault(htmlItalicControl)
|
||||
|
||||
|
||||
fun String.codeMarkdown(): String = markdownDefault(markdownCodeControl)
|
||||
fun String.codeHTML(): String = htmlDefault(htmlCodeControl)
|
||||
|
||||
|
||||
fun String.preMarkdown(): String = markdownDefault(markdownPreControl)
|
||||
fun String.preHTML(): String = htmlDefault(htmlPreControl)
|
||||
|
||||
|
||||
fun String.emailMarkdown(): String = linkMarkdown("mailto://$this")
|
||||
fun String.emailHTML(): String = linkHTML("mailto://$this")
|
||||
|
||||
|
||||
private inline infix fun String.mention(adapt: String.() -> String): String = if (startsWith("@")) {
|
||||
this
|
||||
} else {
|
||||
"@${adapt()}"
|
||||
}
|
||||
|
||||
|
||||
private inline infix fun String.hashTag(adapt: String.() -> String): String = if (startsWith("#")) {
|
||||
this
|
||||
} else {
|
||||
"#${adapt()}"
|
||||
}
|
||||
|
||||
|
||||
infix fun String.mentionMarkdown(userId: UserId): String = linkMarkdown(userId.link)
|
||||
infix fun String.mentionHTML(userId: UserId): String = linkHTML(userId.link)
|
||||
|
||||
|
||||
fun String.mentionMarkdown(): String = mention(String::toMarkdown)
|
||||
fun String.mentionHTML(): String = mention(String::toHtml)
|
||||
|
||||
|
||||
fun String.hashTagMarkdown(): String = hashTag(String::toMarkdown)
|
||||
fun String.hashTagHTML(): String = hashTag(String::toHtml)
|
||||
|
||||
|
||||
fun String.phoneMarkdown(): String = toMarkdown()
|
||||
fun String.phoneHTML(): String = toHtml()
|
||||
|
||||
|
||||
fun String.command(): String = if (startsWith("/")) {
|
||||
this
|
||||
} else {
|
||||
"/$this"
|
||||
}
|
||||
|
||||
fun String.commandMarkdown(): String = command()
|
||||
fun String.commandHTML(): String = command()
|
||||
|
||||
|
||||
infix fun String.bold(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> boldHTML()
|
||||
is Markdown -> boldMarkdown()
|
||||
}
|
||||
|
||||
|
||||
infix fun String.italic(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> italicHTML()
|
||||
is Markdown -> italicMarkdown()
|
||||
}
|
||||
|
||||
infix fun String.hashTag(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> hashTagHTML()
|
||||
is Markdown -> hashTagMarkdown()
|
||||
}
|
||||
|
||||
infix fun String.code(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> codeHTML()
|
||||
is Markdown -> codeMarkdown()
|
||||
}
|
||||
|
||||
infix fun String.pre(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> preHTML()
|
||||
is Markdown -> preMarkdown()
|
||||
}
|
||||
|
||||
infix fun String.email(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> emailHTML()
|
||||
is Markdown -> emailMarkdown()
|
||||
}
|
||||
|
||||
infix fun Pair<String, String>.link(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> first.linkHTML(second)
|
||||
is Markdown -> first.linkMarkdown(second)
|
||||
}
|
||||
|
||||
infix fun String.mention(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> mentionHTML()
|
||||
is Markdown -> mentionMarkdown()
|
||||
}
|
||||
|
||||
infix fun Pair<String, ChatId>.mention(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> first.mentionHTML(second)
|
||||
is Markdown -> first.mentionMarkdown(second)
|
||||
}
|
||||
|
||||
infix fun String.phone(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> phoneHTML()
|
||||
is Markdown -> phoneMarkdown()
|
||||
}
|
||||
|
||||
infix fun String.command(parseMode: ParseMode): String = when (parseMode) {
|
||||
is HTML -> commandHTML()
|
||||
is Markdown -> commandMarkdown()
|
||||
}
|
||||
|
||||
expect fun String.format(vararg args: Any?): String
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils
|
||||
|
||||
class TelegramAPIUrlsKeeper(
|
||||
token: String,
|
||||
hostUrl: String = "https://api.telegram.org"
|
||||
) {
|
||||
val commonAPIUrl = "$hostUrl/bot$token"
|
||||
val fileBaseUrl = "$hostUrl/file/bot$token"
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils.extensions
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestsExecutor
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.bot.exceptions.RequestException
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.Response
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
|
||||
fun <T: Any> RequestsExecutor.executeAsync(
|
||||
request: Request<T>,
|
||||
onFail: (suspend (Response) -> Unit)? = null,
|
||||
scope: CoroutineScope = GlobalScope,
|
||||
onSuccess: (suspend (T) -> Unit)? = null
|
||||
): Job {
|
||||
return scope.launch {
|
||||
try {
|
||||
val result = execute(request)
|
||||
onSuccess ?.invoke(result)
|
||||
} catch (e: RequestException) {
|
||||
onFail ?.invoke(e.response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Any> RequestsExecutor.executeAsync(
|
||||
request: Request<T>,
|
||||
scope: CoroutineScope = GlobalScope
|
||||
): Deferred<T> {
|
||||
return scope.async { execute(request) }
|
||||
}
|
||||
|
||||
suspend fun <T: Any> RequestsExecutor.executeUnsafe(
|
||||
request: Request<T>,
|
||||
retries: Int = 0,
|
||||
retriesDelay: Long = 1000L
|
||||
): T? {
|
||||
var leftRetries = retries
|
||||
do {
|
||||
try {
|
||||
return execute(request)
|
||||
} catch (e: RequestException) {
|
||||
leftRetries--
|
||||
delay(retriesDelay)
|
||||
}
|
||||
} while(leftRetries >= 0)
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils.extensions
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
|
||||
private sealed class DebounceAction<T> {
|
||||
abstract val value: T
|
||||
}
|
||||
|
||||
private data class AddValue<T>(override val value: T) : DebounceAction<T>()
|
||||
private data class RemoveJob<T>(override val value: T, val job: Job) : DebounceAction<T>()
|
||||
|
||||
fun <T> ReceiveChannel<T>.debounceByValue(
|
||||
delayMillis: Long,
|
||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||
resultBroadcastChannelCapacity: Int = 32
|
||||
): ReceiveChannel<T> {
|
||||
val outChannel = Channel<T>(resultBroadcastChannelCapacity)
|
||||
val values = HashMap<T, Job>()
|
||||
|
||||
val channel = Channel<DebounceAction<T>>(Channel.UNLIMITED)
|
||||
scope.launch {
|
||||
for (action in channel) {
|
||||
when (action) {
|
||||
is AddValue -> {
|
||||
val msg = action.value
|
||||
values[msg] ?.cancel()
|
||||
lateinit var job: Job
|
||||
job = launch {
|
||||
delay(delayMillis)
|
||||
|
||||
outChannel.send(msg)
|
||||
channel.send(RemoveJob(msg, job))
|
||||
}
|
||||
values[msg] = job
|
||||
}
|
||||
is RemoveJob -> if (values[action.value] == action.job) {
|
||||
values.remove(action.value)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
for (msg in this@debounceByValue) {
|
||||
channel.send(AddValue(msg))
|
||||
}
|
||||
}
|
||||
|
||||
return outChannel
|
||||
}
|
||||
|
||||
typealias AccumulatedValues<K, V> = Pair<K, List<V>>
|
||||
|
||||
fun <K, V> ReceiveChannel<Pair<K, V>>.accumulateByKey(
|
||||
delayMillis: Long,
|
||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||
resultBroadcastChannelCapacity: Int = 32
|
||||
): ReceiveChannel<AccumulatedValues<K, V>> {
|
||||
val outChannel = Channel<AccumulatedValues<K, V>>(resultBroadcastChannelCapacity)
|
||||
val values = HashMap<K, MutableList<V>>()
|
||||
val jobs = HashMap<K, Job>()
|
||||
|
||||
val channel = Channel<DebounceAction<Pair<K, V>>>(Channel.UNLIMITED)
|
||||
scope.launch {
|
||||
for (action in channel) {
|
||||
val (key, value) = action.value
|
||||
when (action) {
|
||||
is AddValue -> {
|
||||
jobs[key] ?.cancel()
|
||||
(values[key] ?: mutableListOf<V>().also { values[key] = it }).add(value)
|
||||
lateinit var job: Job
|
||||
job = launch {
|
||||
delay(delayMillis)
|
||||
|
||||
values[key] ?.let {
|
||||
outChannel.send(key to it)
|
||||
channel.send(RemoveJob(key to value, job))
|
||||
}
|
||||
}
|
||||
jobs[key] = job
|
||||
}
|
||||
is RemoveJob -> if (values[key] == action.job) {
|
||||
values.remove(key)
|
||||
jobs.remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
for (msg in this@accumulateByKey) {
|
||||
channel.send(AddValue(msg))
|
||||
}
|
||||
}
|
||||
|
||||
return outChannel
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils.extensions
|
||||
|
||||
fun String.toMarkdown(): String {
|
||||
return replace(
|
||||
"*",
|
||||
"\\*"
|
||||
).replace(
|
||||
"_",
|
||||
"\\_"
|
||||
)
|
||||
}
|
||||
|
||||
fun String.toHtml(): String = replace(
|
||||
"<",
|
||||
"<"
|
||||
).replace(
|
||||
">",
|
||||
">"
|
||||
).replace(
|
||||
"&",
|
||||
"&"
|
||||
).replace(
|
||||
"\"",
|
||||
"""
|
||||
)
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.github.insanusmokrassar.TelegramBotAPI.utils.extensions
|
||||
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestsExecutor
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.bot.UpdatesPoller
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.ALL_UPDATES_LIST
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.update.*
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.update.MediaGroupUpdates.*
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.types.update.abstracts.Update
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.updateshandlers.KtorUpdatesPoller
|
||||
import com.github.insanusmokrassar.TelegramBotAPI.updateshandlers.UpdatesFilter
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
typealias UpdateReceiver<T> = suspend (T) -> Unit
|
||||
|
||||
fun RequestsExecutor.startGettingOfUpdates(
|
||||
timeoutMillis: Long = 30 * 1000,
|
||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||
allowedUpdates: List<String>? = null,
|
||||
block: UpdateReceiver<Update>
|
||||
): UpdatesPoller {
|
||||
return KtorUpdatesPoller(
|
||||
this,
|
||||
timeoutMillis.toInt() / 1000,
|
||||
allowedUpdates = allowedUpdates ?: ALL_UPDATES_LIST,
|
||||
updatesReceiver = block
|
||||
).also {
|
||||
it.start(scope)
|
||||
}
|
||||
}
|
||||
|
||||
fun RequestsExecutor.startGettingOfUpdates(
|
||||
messageCallback: UpdateReceiver<MessageUpdate>? = null,
|
||||
messageMediaGroupCallback: UpdateReceiver<MessageMediaGroupUpdate>? = null,
|
||||
editedMessageCallback: UpdateReceiver<EditMessageUpdate>? = null,
|
||||
editedMessageMediaGroupCallback: UpdateReceiver<EditMessageMediaGroupUpdate>? = null,
|
||||
channelPostCallback: UpdateReceiver<ChannelPostUpdate>? = null,
|
||||
channelPostMediaGroupCallback: UpdateReceiver<ChannelPostMediaGroupUpdate>? = null,
|
||||
editedChannelPostCallback: UpdateReceiver<EditChannelPostUpdate>? = null,
|
||||
editedChannelPostMediaGroupCallback: UpdateReceiver<EditChannelPostMediaGroupUpdate>? = null,
|
||||
chosenInlineResultCallback: UpdateReceiver<ChosenInlineResultUpdate>? = null,
|
||||
inlineQueryCallback: UpdateReceiver<InlineQueryUpdate>? = null,
|
||||
callbackQueryCallback: UpdateReceiver<CallbackQueryUpdate>? = null,
|
||||
shippingQueryCallback: UpdateReceiver<ShippingQueryUpdate>? = null,
|
||||
preCheckoutQueryCallback: UpdateReceiver<PreCheckoutQueryUpdate>? = null,
|
||||
pollCallback: UpdateReceiver<PollUpdate>? = null,
|
||||
timeoutMillis: Long = 30 * 1000,
|
||||
scope: CoroutineScope = GlobalScope
|
||||
): UpdatesPoller {
|
||||
val filter = UpdatesFilter(
|
||||
messageCallback,
|
||||
messageMediaGroupCallback,
|
||||
editedMessageCallback,
|
||||
editedMessageMediaGroupCallback,
|
||||
channelPostCallback,
|
||||
channelPostMediaGroupCallback,
|
||||
editedChannelPostCallback,
|
||||
editedChannelPostMediaGroupCallback,
|
||||
chosenInlineResultCallback,
|
||||
inlineQueryCallback,
|
||||
callbackQueryCallback,
|
||||
shippingQueryCallback,
|
||||
preCheckoutQueryCallback,
|
||||
pollCallback
|
||||
)
|
||||
return startGettingOfUpdates(
|
||||
timeoutMillis,
|
||||
scope,
|
||||
filter.allowedUpdates,
|
||||
filter.asUpdateReceiver
|
||||
)
|
||||
}
|
||||
|
||||
fun RequestsExecutor.startGettingOfUpdates(
|
||||
messageCallback: UpdateReceiver<MessageUpdate>? = null,
|
||||
mediaGroupCallback: UpdateReceiver<MediaGroupUpdate>? = null,
|
||||
editedMessageCallback: UpdateReceiver<EditMessageUpdate>? = null,
|
||||
channelPostCallback: UpdateReceiver<ChannelPostUpdate>? = null,
|
||||
editedChannelPostCallback: UpdateReceiver<EditChannelPostUpdate>? = null,
|
||||
chosenInlineResultCallback: UpdateReceiver<ChosenInlineResultUpdate>? = null,
|
||||
inlineQueryCallback: UpdateReceiver<InlineQueryUpdate>? = null,
|
||||
callbackQueryCallback: UpdateReceiver<CallbackQueryUpdate>? = null,
|
||||
shippingQueryCallback: UpdateReceiver<ShippingQueryUpdate>? = null,
|
||||
preCheckoutQueryCallback: UpdateReceiver<PreCheckoutQueryUpdate>? = null,
|
||||
pollCallback: UpdateReceiver<PollUpdate>? = null,
|
||||
timeoutMillis: Long = 30 * 1000,
|
||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||
): UpdatesPoller = startGettingOfUpdates(
|
||||
messageCallback = messageCallback,
|
||||
messageMediaGroupCallback = mediaGroupCallback,
|
||||
editedMessageCallback = editedMessageCallback,
|
||||
editedMessageMediaGroupCallback = mediaGroupCallback,
|
||||
channelPostCallback = channelPostCallback,
|
||||
channelPostMediaGroupCallback = mediaGroupCallback,
|
||||
editedChannelPostCallback = editedChannelPostCallback,
|
||||
editedChannelPostMediaGroupCallback = mediaGroupCallback,
|
||||
chosenInlineResultCallback = chosenInlineResultCallback,
|
||||
inlineQueryCallback = inlineQueryCallback,
|
||||
callbackQueryCallback = callbackQueryCallback,
|
||||
shippingQueryCallback = shippingQueryCallback,
|
||||
preCheckoutQueryCallback = preCheckoutQueryCallback,
|
||||
pollCallback = pollCallback,
|
||||
timeoutMillis = timeoutMillis,
|
||||
scope = scope
|
||||
)
|
||||
Reference in New Issue
Block a user