Skip to content

Usage

See the example projects:

Artifacts

List of all available artifacts:

implementation "com.petersamokhin.vksdk:http-client-jvm-okhttp:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-jvm:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-jvm:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-js:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-js:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-iosX64:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-iosX64:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-iosArm32:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-iosArm32:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-iosArm64:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-iosArm64:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-tvosX64:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-tvosX64:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-tvosArm64:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-tvosArm64:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-watchosX86:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-watchosX86:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-watchosArm32:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-watchosArm32:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-watchosArm64:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-watchosArm64:$vkSdkVersion"

implementation "com.petersamokhin.vksdk:core-macosX64:$vkSdkVersion"
implementation "com.petersamokhin.vksdk:http-client-common-ktor-macosX64:$vkSdkVersion"

Settings

VkSettings

val vkClientSettings = VkSettings(
    // HTTP client is required
    httpClient = httpClient,                    

    // Default is [VkApi.DEFAULT_VERSION], 5.122 for 0.0.7
    // See: https://vk.com/dev/versions
    apiVersion = 5.122,                         

    // Default params are empty
    defaultParams = paramsOf("lang" to "en"),   

    // Default is 3. Provide [VkApi.EXECUTE_MAX_REQUESTS_PER_SECOND_DISABLED] to disable the `execute` queue loop
    maxExecuteRequestsPerSecond = 3,

    // Default is Dispatchers.Default from the kotlin coroutines common module
    // See the mutiplatform example and iOS note for ktor
    backgroundDispatcher = Dispatchers.Default, 

    json = Json { /* your configuration */ }
)

Initialization

This client is used in all the snippets in next sections.

val client = VkApiClient(
    // User or Community id
    id = 151083290,

    // VK API access token. Must have `offline` scope to not have the expire time
    // More: https://vk.com/dev/access_token
    token = "abcdef123456...",

    // Working with the messages API available only for Communities
    type = VkApiClient.Type.Community,

    // See the previous snippet
    settings = vkSettings
)

Also, for server-side applications, useful to do something on user behalf, but it is not safe to send the access token from client. Authorization code flow can help here.

// From here: https://vk.com/apps?act=manage
// Choose the app and get the ID and the other info from app settings
// App ID, Secure key, etc.
val appInfo = AppInfo(
    clientId = 123456789,
    clientSecret = "abcdef12345...",
    redirectUri = "https://oauth.vk.com/blank.html"
)

val client = VkApiClient.fromCode(
    // Code from the client
    code = "abcdef12345...",

    // App info, see above
    app = appInfo,

    // See the previous snippets; settings for the client
    // Client always is the User for the Code flow
    settings = vkSettings
)

API requests

Note about the responses

Usually VK responses contain response field with the requested data. But, when you are using the execute API method, each object from usual response field is placed into array. So, in this case, if you use client.call with batch = true, the unwrapped object will be returned into the callback. If you made the call without putting into the batch queue, the wrapped object is returned. Remember this when you will parse the responses.

You can reuse the requests in this way:

// Request information
val request = client.call("users.get", paramsOf("user_id" to 1))

Synchronous call

// It is always the string, you should parse it by yourself
val response: String = request.execute()

Asynchronous call

request.enqueue(object : Callback<String> {
    override fun onResult(result: String) {
        // It is always the string, you should parse it by yourself
    }

    override fun onError(error: Exception) {
        println("Some error occurred:")
        error.printStackTrace()
    }
})

Batch requests

Requests from the batch queue made as soon as possible based on count of requests in the queue. Using this you cannot exceed the limit of requests per second of VK API. The callback uses JsonElement because VK execute method returning the array of the responses, it is parsed and necessary element is returned to the callback. It can be false in case of an error.

Pass the callback:

client.call(request, batch = true, object : Callback<JsonElement> {
   override fun onResult(result: String) {
       // It is always the JsonElement, you should parse it by yourself
   }

   override fun onError(error: Exception) {
       println("Some error occurred:")
       error.printStackTrace()
   }
})

Or use two callbacks

client.call(request, batch = true, onResult = { result: JsonElement ->
    // It is always the JsonElement, you should parse it by yourself
}, onError = { error: Exception ->
    println("Some error occurred:")
    error.printStackTrace()
})

Also, you can pass parameters, not only the request object

client.call("users.get", paramsOf("user_id" to 1), batch = true, /* callbacks */)

Do as many requests as you want

for (i in 0 until 500) {
    client.call("users.get", paramsOf("user_id" to 1), batch = true, /* callbacks */)
    // without any delay() !
}

Pass all of them in one time:

val requests: List<BatchRequestItem> = listOf(1, 2, 3, 4, 5)
    .map { userId -> client.call("users.get", paramsOf("user_id" to userId)) }
    .map { request -> BatchRequestItem(request, object: Callback<JsonElement> { /* */ }) }

client.call(requests)

Flows for requests

All the functionality based on the Flows is available from the delegate:

val flows: VkApiClientFlows = client.flows()

Other things are the same:

client.flows()
    .call(request, batch = true) // or provide the method name and the parameters
    .onEach { result: JsonElement -> /* do something */ }
    .catch { e -> /* do something */ }
    .flowOn(Dispatchers.IO)
    .launchIn(lifecycleScope)

Send messages

Use the DSL:

client.sendMesage {
    peerId = 12345678901
    message = "Hello, World!"
    attachment = "photo1234_5678,photo1234_1234"

}.execute()

Or use the builder:

val message = Message()
    .peerId(12345678901)
    .text("Hello, World!")

// Then send
val request = client.send(message)

// or else in this way
val request = message.sendFrom(client)

// Don't forget this!
request.execute() // or use flows, callbacks and whatever you want

Keyboard

Use the DSL:

client.sendMessage {
    peerId = 12345678901
    message = "Hello, World!"
    keyboard = keyboard(oneTime = true) { // or `inlineKeyboard` for inline
       row {
           primaryButton("Blue pill") {
               payload = "{}" // etc.
           }
           negativeButton("Red pill")
       }
   }
}.execute()

Attachments

All the functionality needed for the uploading is available from the delegate:

val uploader: VkApiUploader = client.uploader()

Simply attach the image:

val peerId = 12345678901

val imageAttachmentString = client.uploader().uploadPhotoForMessage(
    peerId,
    // or FileOnDisk(path = "/Users/you/Documents/img.png")
    // or ByteArray
    url = "https://example.com/image.png"
)
// imageAttachmentString is something like 'photo987654321_1234'

client.sendMessage {
    peerId = necessaryPeerId
    attachment = imageAttachmentString
}.execute()

Attach whatever you want, e.g audio message:

val necessaryPeerId = 12345678901
val attachmentType = "audio_message"

// VK's process of attachments uploading is boring
// and covered fully by this method,
// but responses are dynamic and can not be serialized in one time for all cases
// See: https://vk.com/dev/upload_files
val docAttachmentString = client.uploader().uploadContent(
    "docs.getMessagesUploadServer", // First step: method for retrieving of the `upload_url`
    "docs.save",                    // Last step: save the attachment on the VK server (can you VK guys do it under the hood?)
    params = paramsOf("type" to attachmentType, "peer_id" to necessaryPeerId),
    items = listOf(
        UploadableContent.File(
            fieldName = "file",
            // Provide these params but don't worry about their values;
            // but without them you will receive the error response.
            fileName = "doesn't matter, but VK API is not stable; fileName must not be empty",
            mediaType = "also doesn't matter",
            file = FileOnDisk("/Users/petersamokhin/Desktop/test.mp3")
        )
    )
).let { response: String ->
    // Request is synchronous. You should parse the response by yourself
    retrieveAttachment(it, attachmentType)
}

client.sendMessage {
    peerId = necessaryPeerId
    // or docAttachmentString, based on your implementation of retrieveAttachment methodd
    attachment = "doc${docAttachmentString}"
}.execute()

Types of items: Bytes array:

val byteArray: ByteArray = retrieveBytesFromSomewhere()

val bytesItem: UploadableContent = UploadableContent.Bytes(
   fieldName = "file",
   fileName = "something.txt",
   mediaType = "text/plain",
   bytes = byteArray
)

File from the filesystem (only available for JVM, darwin and JS):

val fileItem: UploadableContent = UploadableContent.File(
    fieldName = "file",
    fileName = "test.mp3",
    mediaType = "audio/mpeg",
    file = FileOnDisk("/Users/petersamokhin/Desktop/test.mp3")
)

Something by URL:

val urlItem: UploadableContent = UploadableContent.Url(
    fieldName = "file",
    fileName = "image.png",
    mediaType = "image/png",
    url = "https://example.com/image.png"
)

React on events

Use the Bots Long Poll API.

Start listening for the events

The method for starting the listening is blocking, so do not put it at the start of your code or wrap it with the async block.

Start listening for the events:

val settings = VkBotsLongPollApi.Settings(
    wait = 25,   // defaut value; recommended by the VK 
    maxFails = 5 // Use VkBotsLongPollApi.Settings.IGNORE_FAILS to ignore all of the errors
)

// All of the params are optional; don't forget about the note above
client.startLongPolling(restart = false, settings = settings)

Listen for new messages:

client.onMessage { event: MessageNew ->
    if (event.message.isFromChat()) {
        client.sendMessage {
            peerId = event.message.peerId
            message = "Hello, chat"
        }.execute()
    } else {
        client.sendMessage {
            peerId = event.message.peerId
            message = "Sorry, I ignore personal conversations."
        }.execute()
    }
}

Listen for all types of the events:

client.onEachEvent { event: JsonElement ->
    // You should parse the event
}

And, of course, Flow example:

client.flows().onMessage()
    .flatMapLatest {
        if (it.message.isFromChat()) {
            client.flows().sendMessage {
                peerId = it.message.peerId
                message = "Hello, chat"
            } // don't need to execute, etc., because flow is switch-mapped
        } else {
            client.flows().sendMessage {
                peerId = it.message.peerId
                message = "Sorry, I ignore personal conversations."
            } // don't need to execute, etc., because flow is switch-mapped
        }
    }
    .flowOn(Dispatchers.IO)
    .launchIn(this)

HTTP clients

See: https://vksdk.github.io/vk-sdk-kotlin/http-clients/