JetBrains Space Help

Receive Chat Messages

Applications (e.g. chatbots) can interact with Space users through the application chat channel.

In the application chat channel, a Space user can:

In all these cases, Space will send a different type of payload to the application.

To help you get started with chatbots, we prepared a pair of tutorials:

Add a chat channel for the application

To add a chat channel, the application must make the setUiExtensions API call. For example, this is how you can do it on the server side using Kotlin Space SDK:

space.applications.setUiExtensions( contextIdentifier = GlobalPermissionContextIdentifier, extensions = listOf(ChatBotUiExtensionIn()) )

List available commands

When a user starts typing in the application chat channel, Space sends payload of the ListCommandsPayload type to the application.

An example of the payload:

{ "className": "ListCommandsPayload", "accessToken": "", "verificationToken": "abc1234", "userId": "1mEGCd1FvoAh", "serverUrl": "https://mycompany.jetbrains.space", "clientId": "8fd4d79a-d164-4a71-839a-ff8f8bcd6beb", "orgId": "2ulA3W2Vltg6" }

A typical chatbot works in the following way:

  • If a user types the slash / character, Space shows the full list of available commands.

  • If a user types some other character, Space handles the characters as a search pattern and shows the list of commands that match the pattern.

Space provides this functionality out of the box. The only task of the application is to respond with a JSON list of available commands on receiving a ListCommandsPayload. For each command, you should specify its name and description. For example:

{ "commands": [ { "name": "help", "description": "Show this help" }, { "name": "do", "description": "Do something" } ] }

To help you prepare the list of available commands, Space SDK provides the CommandDetail class. For example, this is how you can use it to prepare the list of commands and send it back to Space:

// our custom command that can also 'run' something class Command( val name: String, val description: String, val run: suspend (payload: MessagePayload) -> Unit ) { // convert to CommandDetail fun toCommand() = CommandDetail(name, description) } // list of available commands val commands = listOf( Command( "help", "Show this help", ) { payload -> commandHelp(payload) }, Command( "do", "Do something", ) { payload -> commandDo(payload) } ) // convert the list of 'Command' // to the list of 'CommandDetail' fun commandListAllCommands() = Commands( commands.map { it.toCommand() } ) val jackson = ObjectMapper() fun main() { embeddedServer(Netty, port = 8080) { routing { post("/api/from-space") { val body = call.receiveText() val payload = readPayload(body) // filter payload by class when (payload) { is ListCommandsPayload -> { // respond with a list of commands val cmds = jackson.writeValueAsString(commandListAllCommands()) call.respondText(cmds, ContentType.Application.Json) } is MessagePayload -> { // to be implemented } else -> call.respond(HttpStatusCode.BadRequest, "Unsupported payload type") } } } }.start(wait = true) }

Process message commands

When a user types a message in the application chat and presses Enter, Space sends a payload of the MessagePayload type to the application.

An example of the payload:

{ "className": "MessagePayload", // instance of MessageContext class "message": { "messageId": "Cxa000Cxa", // channelId that you can use to get channel name "channelId": "3FhQeS2URbeY", "messageData": null, "body": { "className": "ChatMessage.Text", "text": "do 1234" }, "attachments": null, "externalId": null, "createdTime": "2021-05-27T10:05:03.796Z" }, "accessToken": "", "verificationToken": "abc1234", "userId": "1mEGCd1FvoAh", "serverUrl": "https://mycompany.jetbrains.space", "clientId": "8fd4d79a-d164-4a71-839a-ff8f8bcd6beb", "orgId": "2ulA3W2Vltg6" }

If your application is a chatbot or a slash command, typically, it implies that a user must send some command to the application chat. This can be just a command name, for example, help, or a command with arguments, for example, book room-621. The MessagePayload class provides two helper functions for processing commands sent in the messages:

  • MessagePayload.command(): String? returns the command specified by the user

  • MessagePayload.commandArguments(): String? returns the command arguments specified by the user

// For example, a user sends 'do 1234' to chat val cmd = payload.command() // do val cmdargs = payload.commandArguments() // 1234

To help you work with different types of message attachments, Space SDK provides the following classes:

  • interface AttachmentIn : Attachment

  • interface LinkPreview : Attachment

  • interface MediaAttachment : AttachmentIn

  • DeletedAttachment( deletedIdentity: PropertyValue<String> ) : Attachment

  • FileAttachment( id: PropertyValue<String>, sizeBytes: PropertyValue<Long>, filename: PropertyValue<String> ) : AttachmentIn

  • ImageAttachment( id: PropertyValue<String>, name: PropertyValue<String?>, width: PropertyValue<Int>, height: PropertyValue<Int>, previewBytes: PropertyValue<String?>, variants: PropertyValue<List<ImageAttachmentVariant>?> ) : MediaAttachment

  • ProfileLinkPreview( profile: PropertyValue<TD_MemberProfile> ) : Attachment

  • UnfurlAttachment( unfurl: PropertyValue<Unfurl>, id: PropertyValue<String> ) : Attachment

  • VideoAttachment( id: PropertyValue<String>, name: PropertyValue<String?>, width: PropertyValue<Int?>, height: PropertyValue<Int?>, sizeBytes: PropertyValue<Long>, previewBytes: PropertyValue<String?> ) : MediaAttachment

For example, this is how you can handle messages with Kotlin Space SDK:

// our custom command that can also 'run' something class Command( val name: String, val description: String, val run: suspend (payload: MessagePayload) -> Unit ) { // convert to CommandDetail fun toCommand() = CommandDetail(name, description) } // list of available commands val commands = listOf( Command( "help", "Show this help", ) { payload -> commandHelp(payload) }, Command( "do", "Do something", ) { payload -> commandDo(payload) } ) // convert the list of 'Command' // to the list of 'CommandDetail' fun commandListAllCommands() = Commands( commands.map { it.toCommand() } ) fun main() { embeddedServer(Netty, port = 8080) { routing { post("/api/from-space") { val body = call.receiveText() val payload = readPayload(body) // filter payload by class when (payload) { is MessagePayload -> { // find the command val command = commands.find { it.name == payload.command() } if (command == null) { commandHelp() } else { launch { command.run(payload) } } call.respond(HttpStatusCode.OK, "") } // here goes handling of other payloads else -> call.respond(HttpStatusCode.BadRequest, "Unsupported payload type") } } } }.start(wait = true) } suspend fun commandHelp() { // send help message } suspend fun commandDo(payload: MessagePayload) { val args = payload.commandArguments() // do something with args }

Process user actions in messages

Message constructor DSL lets you create chat messages containing interactive UI controls (currently, only buttons are supported). The controls have the action: MessageAction property. The MessageAction interface has one implementation: PostMessageAction(actionId: String, payload: String). When a user clicks a button in the chat, Space sends a payload of the MessageActionPayload type to the application. The payload contains button's actionId and action's payload. After performing this action, the application must respond with the 200 OK HTTP status.

An example of the payload:

{ "className": "MessageActionPayload", // action associated to the clicked button "actionId": "do", // action payload "actionValue": "that!", // original message is sent back to the application "message": { "messageId": "208N00208N", "channelId": "2YtE2D2TDGIe", "messageData": null, "body": { "className": "ChatMessage.Block", "style": "PRIMARY", "outline": null, "sections": [ { "className": "MessageSection", "header": "Let's do something", "elements": [ { "className": "MessageControlGroup", "elements": [ { "className": "MessageButton", "text": "Do this!", "style": "PRIMARY", "action": { "className": "PostMessageAction", "actionId": "do", "payload": "this!" }, "disabled": false }, { "className": "MessageButton", "text": "Do that!", "style": "PRIMARY", "action": { "className": "PostMessageAction", "actionId": "do", "payload": "that!" }, "disabled": false } ] } ], "footer": null } ], "messageData": null }, "attachments": null, "externalId": null, "createdTime": "2021-06-10T09:36:49.479Z" }, "accessToken": "", "verificationToken": "abc1234", "userId": "1sqTwZ1MEJM1", "serverUrl": "https://mycompany.jetbrains.space", "clientId": "9c4ecb1d-e160-4f21-9622-5b1ff1f2b2cc", "orgId": "RpfEk1hMPl2" }

In case of Space SDK, use the MessageActionPayload class to process message actions. The class provides two properties:

  • actionId: String returns the actionId of the clicked button.

  • actionValue: String returns the action payload.

// For example, a user clicks a button with // 'actionId="do"' and 'payload="that!"' val id = payload.actionId // do val value = payload.actionValue // that!

Here's how you can process message actions with Kotlin Space SDK:

fun main() { embeddedServer(Netty, port = 8080) { routing { post("/api/from-space") { val body = call.receiveText() val payload = readPayload(body) // filter payload by class when (payload) { is MessageActionPayload -> { // filter by action Id when (payload.actionId) { "do" -> { commandDo(context, payload) } else -> error("Unknown command ${payload.actionId}") } // After sending a command, Space will wait for OK confirmation call.respond(HttpStatusCode.OK, "") } // here goes handling of other payloads else -> call.respond(HttpStatusCode.BadRequest, "Unsupported payload type") } } } }.start(wait = true) } suspend fun commandDo(context: CallContext, payload: MessageActionPayload) { val value = payload.actionValue // in our example we just send a message back to the user sendMessage(context, doMessage(value)) } fun doMessage(text: String): ChatMessage { return message { section { header = "I'm doing $text" } } }
Last modified: 08 August 2022