JetBrains Space Help

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.

Add a chat channel for the application

Adding a chat channel for an application is an explicit action. Multi-org applications can add a chat channel for themselves by making 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()) )

In case of a single-org application, you can enable a chat channel from the application settings:

  1. In Extensions | Applications, open the required application.

  2. In the Overview tab, select Chat bot.

  3. If you want to open the corresponding chat channel, click Go to chat bot.

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 several classes, for example, FileAttachment, ImageAttachment, VideoAttachment, and others. Learn how to upload and attach files

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: 09 August 2023