Привет, Хабр. Недавно JetBrains представили свой новый продукт под названием Space, о чем был своевременный пост на Хабре. Прошло немного времени и уже пора бы попробовать некоторые его особенности как платформы. В этой статье речь пойдет о Space Applications. Space Applications - это расширения серверной и клиентской частей. Applications позволяют взаимодействовать с разными компонентами платформы и расширять её функциональность. Первым делом рассмотрим расширения серверной части, которые предоставляет Space. Основное ограничение - Application нельзя запустить непосредственно внутри окружения Space. Для предоставления доступа между Space и Application используется так называемый Endpoint, в котором мы указываем Endpoint URI - адрес нашего плагина, на который Space будет отправлять запросы и коммуницировать с помощью Space HTTP API. На данный момент известно несколько видов Applications: Cтандартный чатбот, с которым взаимодействие происходит в приватном с ним чате. На данный момент кастомизация сильно уступает, например, Telegram. Нельзя добавлять бота в канал (групповой чат), а из интерактивного интерфейса пока доступны только кнопки.        Для каждого канала могут быть заданы разные команды, и когда пользователь вводит "/", ему показывается список доступных команд. На данный момент доступны только для чатботов.     Используют функционал Space, чтобы получать от него разную информацию или взаимодействовать с разными модулями и компонентами, например, открывать/закрывать issues, писать сообщения, отправлять посты в канал и так далее.     Позволяют расширить стандартные меню новыми элементами. На момент написания статьи api для них еще не доступен.     Также можно свободно комбинировать сразу несколько типов Applications. Например, создать бота, у которого будет кнопка для запуска билда проекта на CI/CD сервере, и результат которого вернется в общий канал. Для начала Application требуется создать. Делается это через Administration menu. При создании можно указать, какими правами оно будет обладать, чтобы приложение имело доступ только к тем ресурсам, которые нами явно указаны. После создания в табе Authentication требуется выбрать один из возможных Authentication flows, в зависимости от типа приложения. Кратко каждый из них можно описать так: Client Credentials Flow - самый простой способ. Работает через client id и client secret. Наше приложение будет работать от своего лица и не сможет получить доступ к некоторым компонентам платформы. Используется в полностью серверном приложении. Authorization Code Flow - логинимся в приложение через Space, получаем код авторизации, который приложение использует чтобы из него получить токен и работать от лица пользователя.   Implicit Flow - идея таже, что и Authorization Code Flow, только клиент логинится на стороне браузера. В данном разделе мы используем функционал Space, чтобы получить от сервера разную информацию: каналы, приложения, пользователи, проекты, и т.д.  Сначала нам требуется создать и настроить клиент. Мы будем использовать Client Credentials Flow: Ниже приведены примеры некоторых расширений и запросов. В зависимости от настроенных прав полученные данные могут отличаться. Стоит обратить внимание, что почти все данные возвращаются через .data. Изначально возвращается объект Batch, который используется для пагинации и содержит текущий набор данных, ключ к следующему набору, и общее количество элементов. При запросах стоит это учитывать, однако для простоты в данной статье пагинация опущена. Исходники проекта доступны на github и со временем будут пополняться. Серверные Applications, по сравнению с клиентскими, требуют дополнительной подготовки. Первым делом нам нужно серверное приложение, на котором будет хоститься наш бот. Мы уже использовали HTTP клиент Ktor, так что можем создавать на нем и серверную часть. Создание конфигурационных файлов для Ktor здесь будет опущено, но есть документация, на которую можно опереться. Когда мы поднимем наш сервер, мы сможем получить его URL, чтобы Space мог его использовать. Покупать хост, или искать бесплатный не комильфо, поэтому мы пойдем обходным путем - будем использовать туннельный сервис. В официальной документации используется ngrok, однако лично я предпочитаю localtunnel, из-за его возможности указывать постоянный адрес. В любом случае мы получим URL - это как раз то, что нам нужно. Возвращаемся в настройки Applications в нашем Space. В табе Endpoint в поле Endpoint URI нужно будет указать наш url. Для меня это https://makentoshe.loca.lt/api/chatbot. Про то, зачем нужен /api/chatbot будет дальше. В том же табе существует два способа верифицировать наши запросы:   Verification token - этот токен кладется в каждый запрос от Space. Нам остается сравнить эти токены, и если они совпадают - мы общаемся с нашим Space.  Signing key - более продвинутый метод. Для каждого запроса создается хеш, который кладется в его заголовок. Когда мы получаем запрос мы также вычисляем хеш, и если они совпадают - все ок. Подробнее об алгоритме - здесь. В качестве примера мы возьмем оба варианта и будем сверять и хеши, и токены. Первым делом для сервера нам нужно указать Routing. Это тот самый Endpoint для Space, который он будет использовать, чтобы обращаться к нашему боту. Как только к серверу происходит обращение по заданному адресу, первым делом мы проверяем, что запрос пришел именно от нашего Space. Если это не так - возвращаем 401 Unauthorized. Далее боту следует обработать полученный payload. На момент написания статьи существует 5 имплементаций ApplicationPayload: MessagePayload - передается нам, когда пользователь отправляет обычное сообщение. ListCommandsPayload - передается нам, когда пользователь начинает сообщение с "/" и затем вводит команду посимвольно. Здесь происходит запрос все существующих команд, которые нам нужно будет вернуть в виде json. На ввод каждого символа передается новый Payload. MessageActionPayload - передается, когда пользователь нажимает на интерактивный элемент сообщения, например, на кнопку. Корректно срабатывают только элементы из последнего сообщения. Все предыдущие элементы будут "стерты"(например, их actionId всегда будут пустой строкой). MenuActionPayload - передается, когда пользователь нажимает на кастомный элемент на одном из меню (ProjectMenu, LocationMenu, ChannelMessageMenu, ChannelAttachmentMenu и т.д.). На момент написания статьи все еще не доступен. ListMenuExtensionsPayload - имеет ту же идею, что и ListCommandsPayload, только для меню. На момент написания статьи все еще не доступен. Когда пользователь отправляет нам сообщение - скорее всего это команда, которую боту надо выполнить. Команду можно описать одним дата классом:   Предлагаю в качестве примера реализовать три простых команды:  help - выводит сообщение со списком всех команд; echo - выводит переданное сообщение обратно пользователю; interactive <type> - выводит пример выбранного типа интерактивного элемента, например, button. Сначала разберемся, что происходит в командах help и echo. Из ApplicationPayload каждой команды мы можем извлечь нужные данные для обработки и положить их в соответствующий Context, который мы сами определяем. Этот класс помогает нам аккумулировать данные в одном месте. После того, как мы получили наш userId и остальные сопутствующие данные, нам нужно их вывести в чат пользователя, с которым в данный момент взаимодействует бот. Для этого напишем   отдельную функцию printToChat.  Любое сообщение в Space представлено в виде класса ChatMessage. Этот класс является sealed и имеет 2 наследника: ChatMessage.Text и ChatMessage.Block.   ChatMessage.Text является простым текстовым сообщением поддерживающим markdown.  ChatMessage.Block существует для более сложных сообщений, которые могут быть разбиты на секции, иметь разделители, и другие продвинутые элементы форматирования. Для него существует специальный DSL, который мы и используем. Всё начинается с функции message - это корень нашего сообщения, в котором мы можем указать: MessageOutline - это дополнительная подпись под именем отправителя и, по желанию, иконка, которая задается через строку. Какой именно должна быть эта строка пока не понятно, поэтому вместо иконки передаем null. MessageStyle - изменяет некоторые цвета в сообщении, в соответствии со стилем. Для чего нужна messageData я пока так и не понял. Чтобы не было передано в сообщении нашему боту, это поле всегда будет null. После этого мы можем либо определить новую секцию методом section, либо поставить разделитель методом divide. В секции нам доступны: обычное текстовое поле через метод text. текстовое поле с тегом, для которого можно добавить отдельный стиль. текстовое поле с изображением справа от секции. текстовое поле с иконкой, в которую опять же передается строка. footer и header. конструкция “поле    значение” через fields интерактивные элементы, из которых на момент написания статьи доступна только кнопка. Как это выглядит:  Осталось реализовать команду interaction и соединить все вместе. Теперь мы можем подключать обработку разных ApplicationPayload's. Для простого сообщения от пользователя мы ищем команду из списка и выполняем её.   Для показа списка команд мы возвращаем список всех команд. Здесь же мы используем подключенный ранее Jackson.  Для нажатия на интерактивный элемент мы будем принтить actionId этой команды. Исходники чатбота доступны по ссылке на github. Почти все, что может сделать пользователь используя Space через пользовательский интерфейс, можно сделать и через предоставленный Api(если на то будут предоставлены разрешения).  На самом деле у Space гораздо больше способов расширения функционала. Мы рассмотрели лишь самый продвинутый уровень. Большую часть остальных расширений можно настроить имея вообще минимальные навыки программирования.  Вот остальные способы расширения функционала платформы:  Webhooks - оповещение внешних сервисов о событиях в Space. Authorizations - доступ к Space через OAuth 2.0. Custom fields - добавление кастомных полей к сущностям Space. Скрипты для импорта данных из других приложений. Сторонние интеграции с инструментами. Неполноценный разработчик