All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.stormbit.vksdk.longpoll.LongPoll.kt Maven / Gradle / Ivy

The newest version!
package com.github.stormbit.vksdk.longpoll

import com.github.stormbit.vksdk.clients.Client
import com.github.stormbit.vksdk.clients.GroupClient
import com.github.stormbit.vksdk.events.Event
import com.github.stormbit.vksdk.longpoll.updateshandlers.UpdatesHandler
import com.github.stormbit.vksdk.longpoll.updateshandlers.UpdatesHandlerGroup
import com.github.stormbit.vksdk.longpoll.updateshandlers.UpdatesHandlerUser
import com.github.stormbit.vksdk.objects.models.LongPollServerResponse
import com.github.stormbit.vksdk.utils.*
import com.github.stormbit.vksdk.vkapi.execute
import io.ktor.client.features.*
import io.ktor.client.request.*
import kotlinx.coroutines.*
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import org.slf4j.LoggerFactory
import kotlin.reflect.KClass

@Suppress("unused")
class LongPoll(private val client: Client) {
    private val log = LoggerFactory.getLogger(LongPoll::class.java)

    private val wait = 25

    /**
     * 2 + 32 + 128
     * items + pts + random_id
     */
    private val mode = 162

    private var version = 3

    @Volatile
    private var isLongPollStarted = false

    @Suppress("UNCHECKED_CAST")
    private val updatesHandler: UpdatesHandler = when (client) {
        is GroupClient -> UpdatesHandlerGroup(client) as UpdatesHandler

        else -> UpdatesHandlerUser(client) as UpdatesHandler
    }

    fun start() = runBlocking {
        val handler = CoroutineExceptionHandler { _, e ->
            log.error("Some error occurred: ", e)
        }

        launch(handler) {
            updatesHandler.start()
        }

        if (!isLongPollStarted) {
            isLongPollStarted = true

            launch(handler) {
                startListening()
            }
        }
    }

    /**
     * If you need to set new LongPoll server, or restart listening
     * stop old before.
     */
    fun stop() {
        isLongPollStarted = false
    }

    fun  registerEvent(callback: suspend T.() -> Unit, type: KClass) {
        updatesHandler.registerEvent(callback, type)
    }

    private suspend fun setData(): LongPollServerResponse {
        val serverResponse = try {
            when (client) {
                is GroupClient -> getLongPollServerGroup(client.id)

                else -> getLongPollServer()
            }
        } catch (e: Exception) {
            log.error("Some error occurred when trying to get LongPoll settings, aborting. Trying again in 1 sec.")
            delay(1000)

            return setData()
        }

        var (server, key, pts, ts) = serverResponse

        if (!server.startsWith("https://")) {
            server = "https://$server"
        }

        return LongPollServerResponse(server, key, pts, ts)
    }

    private suspend fun getLongPollServer(): LongPollServerResponse {
        val result = client.messages.getLongPollServer(true, version).execute()

        log.info("LongPollServerResponse: \n$result\n")

        return result
    }

    private suspend fun getLongPollServerGroup(groupId: Int): LongPollServerResponse {
        val result = client.groups.getLongPollServer(groupId).execute()

        log.info("LongPollServerResponse: \n$result\n")

        return result
    }

    private suspend fun startListening() {
        var (server, key, _, ts) = setData()

        log.info("Started listening to events senderType VK LongPoll server...")

        while (isLongPollStarted) {
            var response: JsonObject?

            try {
                val url = "$server?act=a_check&key=$key&ts=$ts&wait=$wait&mode=$mode&version=$version&msgs_limit=100000"

                val responseString = longPollRequest(url) ?: continue

                response = responseString.toJsonObject()

            } catch (e: SerializationException) {
                log.error("Some error occurred, no updates got senderType LongPoll server: ", e)

                delay(1000)

                continue
            }

            if (response.containsKey("failed")) {
                val code = response.getInt("failed")

                log.error("Response of VK LongPoll fallen with error code $code")

                if (code == 4) {
                    version = response.getInt("max_version")!!
                }

                val (_server, _key, _, _ts) = setData()

                server = _server
                key = _key
                ts = _ts
            } else {
                if (response.containsKey("ts")) ts = response.getString("ts")

                if (response.containsKey("ts") && response.containsKey("updates")) {
                    val updates = response.getJsonArray("updates")!!

                    if (updates.isNotEmpty()) {
                        updatesHandler.send(updates[0])
                    }
                } else {
                    log.error("Bad response senderType VK LongPoll server: no 'ts' or 'updates' array: $response")

                    delay(1000)
                }
            }
        }
    }

    private suspend fun longPollRequest(url: String): String? {
        return try {
            client.httpClient.get(url) {
                header("Accept-Charset", "utf-8")
                timeout {
                    connectTimeoutMillis = 30000
                    requestTimeoutMillis = 30000
                }
            }
        } catch (e: TimeoutCancellationException) {
            null
        }
    }

    /**
     * If the client need to start typing
     * after receiving message
     * and until client's message is sent
     * @param enable true or false
     */
    fun enableTyping(enable: Boolean) {
        updatesHandler.sendTyping = enable
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy