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

com.github.stormbit.sdk.utils.Utils.kt Maven / Gradle / Ivy

@file:Suppress("unused", "UNCHECKED_CAST", "RegExpRedundantEscape")

package com.github.stormbit.sdk.utils

import com.github.stormbit.sdk.callbacks.Callback
import com.github.stormbit.sdk.clients.Client
import com.github.stormbit.sdk.objects.Chat
import com.github.stormbit.sdk.utils.Utils.Companion.EnumDescriptor
import com.github.stormbit.sdk.utils.vkapi.Auth
import com.github.stormbit.sdk.utils.vkapi.methods.Address
import com.github.stormbit.sdk.utils.vkapi.methods.Attachment
import com.github.stormbit.sdk.utils.vkapi.methods.Media
import com.google.gson.*
import com.google.gson.internal.LinkedTreeMap
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import io.ktor.util.date.*
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.ClassSerialDescriptorBuilder
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import java.io.*
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLConnection
import java.net.URLEncoder
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.collections.HashMap
import kotlin.collections.set
import kotlin.reflect.KClass

internal class CustomizedObjectTypeAdapter : TypeAdapter() {
    private val delegate = Gson().getAdapter(Any::class.java)

    @Throws(IOException::class)
    override fun write(out: JsonWriter, value: Any?) {
        delegate.write(out, value)
    }

    companion object {
        val FACTORY = object : TypeAdapterFactory {
            override fun  create(gson: Gson, type: TypeToken): TypeAdapter? {
                return if (MutableMap::class.java.isAssignableFrom(type.rawType)) {
                    (CustomizedObjectTypeAdapter() as TypeAdapter)
                } else null
            }
        }
    }

    @Throws(IOException::class)
    override fun read(reader: JsonReader): Any? {
        return when (reader.peek()) {
            JsonToken.BEGIN_ARRAY -> {
                val list: MutableList = ArrayList()
                reader.beginArray()
                while (reader.hasNext()) {
                    list.add(read(reader))
                }
                reader.endArray()
                list
            }

            JsonToken.BEGIN_OBJECT -> {
                val map: MutableMap = LinkedTreeMap()
                reader.beginObject()
                while (reader.hasNext()) {
                    map[reader.nextName()] = read(reader)
                }
                reader.endObject()
                map
            }

            JsonToken.STRING -> reader.nextString()

            JsonToken.NUMBER -> {
                //return in.nextDouble();
                val n = reader.nextString()
                if (n.indexOf('.') != -1) {
                    n.toDouble()
                } else n.toLong()
            }

            JsonToken.BOOLEAN -> reader.nextBoolean()

            JsonToken.NULL -> {
                reader.nextNull()
                null
            }

            else -> throw IllegalStateException()
        }
    }
}

internal val json = Json {
    encodeDefaults = false
    ignoreUnknownKeys = true
    isLenient = false
    allowStructuredMapKeys = true
    prettyPrint = false
    useArrayPolymorphism = false
}

private val adapter = CustomizedObjectTypeAdapter()

val gson = GsonBuilder()
        .serializeNulls()
        .registerTypeAdapterFactory(CustomizedObjectTypeAdapter.FACTORY)
        .setPrettyPrinting().create()

class Utils {
    companion object {
        val hashes = JsonObject()
        const val version = 5.122
        const val userApiUrl = "https://vk.com/dev"

        val RE_CAPTCHAID = Regex("onLoginCaptcha\\('(\\d+)'")
        val AUTH_HASH = Regex("\\{.*?act: 'a_authcheck_code'.+?hash: '([a-z_0-9]+)'.*\\}")

        /**
         * Analog method of 'shift()' method from javascript
         *
         * @return First element of list, and then remove it
         */
        fun  CopyOnWriteArrayList.shift(): T? {
            if (this.size > 0) {
                val item = this[0]
                this.removeAt(0)
                return item
            }

            return null
        }

        /**
         * @param photos JSONArray with photo objects
         * @return URL of biggest image file
         */
        fun getBiggestPhotoUrl(photos: JsonArray): String {
            val currentBiggestPhoto: String

            val sizes: MutableMap = HashMap()

            for (obj in photos) {
                if (obj is JsonObject) {
                    val width = obj.getInt("width")
                    val url = obj.getString("url")
                    sizes[width] = url
                }
            }

            currentBiggestPhoto = sizes.getValue(Collections.max(sizes.keys))
            return currentBiggestPhoto
        }

        fun Boolean.asInt(): Int {
            return if (this) 1 else 0
        }

        fun toJsonObject(string: String): JsonObject = JsonParser.parseString(string).asJsonObject

        fun toJsonObject(map: Map?): JsonObject = gson.toJsonTree(map).asJsonObject

        fun String.callSync(client: Client, params: JsonObject?): JsonObject = client.api.callSync(this, params)
        fun String.callSync(client: Client, vararg params: Any?): JsonObject = client.api.callSync(this, *params)

        fun String.call(client: Client, params: JsonObject?, callback: Callback) = client.api.call(this, params, callback)
        fun String.call(client: Client, callback: Callback, vararg params: Any?) = client.api.call(this, callback, *params)

        fun JsonObject.map(): Map {
            val type = object : TypeToken>() {}.type
            val result = gson.fromJson>(this, type).filter {
                it.value != null
            } as Map
            return result
        }

        /**
         * Convert params query to map
         *
         * @param query query
         * @return JSONObject query
         */
        fun explodeQuery(query: String): JsonObject {
            var query = query

            query = URLEncoder.encode(query, "UTF-8")

            val map: MutableMap = HashMap()

            val arr = query.split("&".toRegex()).toTypedArray()

            for (param in arr) {
                val tmp_arr = param.split("=".toRegex())
                val key = tmp_arr[0]
                val value = tmp_arr[1]

                if (tmp_arr[1].contains(",")) {
                    map[key] = gson.toJsonTree(listOf(*value.split(",".toRegex()).toTypedArray())).asJsonArray
                } else {
                    map[key] = value
                }
            }

            return toJsonObject(map)
        }

        fun getHash(auth: Auth, method: String) {
            val html = auth.session.get("https://vk.com/dev/$method").send().readToText()
            val hash_0 = regexSearch("onclick=\"Dev.methodRun\\('(.+?)', this\\);", html, 1)

            check(hash_0!!.isNotEmpty()) { "Method is not valid" }

            hashes.addProperty(method, hash_0)
        }

        fun regexSearch(pattern: Regex, string: String, group: Int = 0): String? {
            return pattern.find(string)?.groups?.get(group)?.value
        }

        fun regexSearch(pattern: String, string: String, group: Int = 0): String? {
            return Regex(pattern).find(string)?.groups?.get(group)?.value
        }

        fun getId(client: Client): Int {
            val response: JsonObject = client.users.get().getAsJsonArray("response").getJsonObject(0)

            return response.getInt("id")
        }

        fun guessFileNameByContentType(contentType: String): String {
            var contentType = contentType

            contentType = contentType
                    .replace("mpeg", "mp3")
                    .replace("svg+xml", "svg")
                    .replace("javascript", "js")
                    .replace("plain", "txt")
                    .replace("markdown", "md")

            val mainType = contentType.substring(0, contentType.indexOf('/'))

            if (contentType.contains(" ")) {
                contentType = contentType.substring(0, contentType.indexOf(' '))
            }

            var subType = contentType.substring(contentType.lastIndexOf('/') + 1)

            if (subType.contains("-") || subType.contains(".") || subType.contains("+")) subType = "unknown"

            return "$mainType.$subType"
        }

        @Throws(IOException::class)
        fun getMimeType(bytes: ByteArray?): String {
            val `is`: InputStream = BufferedInputStream(ByteArrayInputStream(bytes))

            var mimeType = URLConnection.guessContentTypeFromStream(`is`)

            mimeType = mimeType.substring(mimeType.lastIndexOf('/') + 1).replace("jpeg", "jpg")

            return mimeType
        }

        fun close(conn: URLConnection) {
            if (conn is HttpURLConnection) {
                conn.disconnect()
            }
        }

        @Throws(IOException::class)
        fun copy(input: InputStream, output: OutputStream): Int {
            val count = copyLarge(input, output)
            return if (count > 2147483647L) -1 else count.toInt()
        }

        @Throws(IOException::class)
        fun copyLarge(input: InputStream, output: OutputStream): Long {
            return copy(input, output, 4096)
        }

        @Throws(IOException::class)
        fun copy(input: InputStream, output: OutputStream, bufferSize: Int): Long {
            return copyLarge(input, output, ByteArray(bufferSize))
        }

        @Throws(IOException::class)
        fun copyLarge(input: InputStream, output: OutputStream, buffer: ByteArray): Long {
            var n: Int

            var count = 0L

            while (input.read(buffer).also { n = it } != -1) {
                output.write(buffer, 0, n)
                count += n.toLong()
            }

            return count
        }

        /**
         * Methods from commons-lang library of Apache
         * Added to not use the library for several methods
         */
        @Throws(IOException::class)
        fun toByteArray(url: URL): ByteArray {
            val conn = url.openConnection()
            val var2: ByteArray
            var2 = try {
                toByteArray(conn)
            } finally {
                close(conn)
            }
            return var2
        }

        @Throws(IOException::class)
        fun toByteArray(urlConn: URLConnection): ByteArray {
            val inputStream = urlConn.getInputStream()
            val var2 = toByteArray(inputStream)

            return var2
        }

        @Throws(IOException::class)
        fun toByteArray(input: InputStream): ByteArray {
            val output = ByteArrayOutputStream()
            copy(input, output)
            return output.toByteArray()
        }

        interface IntEnum {
            val value: Int
        }

        internal inline val GMTDate.unixtime: Int
            get() = (GMTDate(seconds, minutes, hours, dayOfMonth, month, year).timestamp / 1000).toInt()

        fun GMTDate.toDMYString(): String = buildString {
            append(dayOfMonth.toString().padStart(2, '0'))
            append((month.ordinal + 1).toString().padStart(2, '0'))
            append(year.toString().padStart(4, '0'))
        }

        @ExperimentalSerializationApi
        @Suppress("FunctionName")
        fun > EnumDescriptor(clazz: KClass, cases: Array): SerialDescriptor {
            return buildClassSerialDescriptor(clazz.simpleName ?: "") {
                for (case in cases) element(case.name, descriptor(case))
            }
        }

        @ExperimentalSerializationApi
        private fun > ClassSerialDescriptorBuilder.descriptor(case: E): SerialDescriptor {
            return buildClassSerialDescriptor("$serialName.${case.name}")
        }
    }
}

abstract class EnumIntSerializer(clazz: KClass, cases: Array) : KSerializer where E : Enum, E : Utils.Companion.IntEnum {
    private val caseByInt: Map = cases.associateBy(Utils.Companion.IntEnum::value)
    override val descriptor: SerialDescriptor = EnumDescriptor(clazz, cases)
    override fun serialize(encoder: Encoder, value: E) = encoder.encodeInt(value.value)
    override fun deserialize(decoder: Decoder): E = caseByInt.getValue(decoder.decodeInt())
}

internal fun Address.Timetable.serialize(): String = json.encodeToString(Address.Timetable.serializer(), this)

inline val Media.mediaString: String
    get() = buildString {
        append(ownerId)
        append("_").append(id)
        if (accessKey != null) append("_").append(accessKey!!)
    }

inline val Attachment.attachmentString: String
    get() = buildString {
        append(typeAttachment.value)
        append(mediaString)
    }

fun JsonObject.getString(key: String): String = this[key].asString
fun JsonObject.getInt(key: String): Int = this[key].asInt
fun JsonObject.getLong(key: String): Long = this[key].asLong
fun JsonObject.getBoolean(key: String): Boolean = this[key].asBoolean

fun JsonArray.getString(index: Int): String = this[index].asString
fun JsonArray.getInt(index: Int): Int = this[index].asInt
fun JsonArray.getJsonArray(index: Int): JsonArray = this[index].asJsonArray
fun JsonArray.getJsonObject(index: Int): JsonObject = this[index].asJsonObject


fun JsonObject.put(key: String, value: String?): JsonObject {
    this.addProperty(key, value)
    return this
}

fun JsonObject.put(key: String, value: Number?): JsonObject {
    this.addProperty(key, value)
    return this
}

fun JsonObject.put(key: String, value: Char?): JsonObject {
    this.addProperty(key, value)
    return this
}

fun JsonObject.put(key: String, value: Boolean?): JsonObject {
    this.addProperty(key, value)
    return this
}

fun JsonObject.put(key: String, value: JsonElement?): JsonObject {
    this.add(key, value)
    return this
}

inline val Int.peerIdToGroupId: Int get() = -this
inline val Int.peerIdToChatId: Int get() = this - Chat.CHAT_PREFIX

inline val Int.groupIdToPeerId: Int get() = -this
inline val Int.chatIdToPeerId: Int get() = this + Chat.CHAT_PREFIX

inline val Int.isGroupId: Boolean get() = this < 0
inline val Int.isChatPeerId: Boolean get() = this > Chat.CHAT_PREFIX
inline val Int.isEmailPeerId: Boolean get() = this < 0 && (-this).isChatPeerId
inline val Int.isUserPeerId: Boolean get() = !isGroupId && !isChatPeerId && !isEmailPeerId




© 2015 - 2025 Weber Informatics LLC | Privacy Policy