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:
In Extensions | Applications, open the required application.
In the Overview tab, select Chat bot.
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:
// 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:
// 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