Alice-ktx
— это библиотека на Kotlin, упрощающая разработку навыков Алисы из Яндекс.Диалогов. В этой статье мы рассмотрим основные возможности библиотеки.
Установка
Для начала, добавьте библиотеку в зависимости вашего проекта
dependencies { implementation("io.github.danbeldev:alice-ktx:0.0.3") }
Первый навык, Эхо‑бот
fun main() { skill { id = "..." webServer = ktorWebServer { port = 8080 path = "/alice" } dispatch { message({ message.session.new }) { response { text = "Привет!" } } message { response { text = message.request.command.toString() } } } }.run() }
-
id
— Уникальный идентификатор скилла, читайте здесь. -
webServer
— Конфигурация приложение с использованием Ktor. -
port
— Порт, на котором будет запущено приложение. В данном случае используется порт 8080. -
path
— Путь, по которому приложение будет доступен. В данном случае это /alice.
message({ message.session.new }) { response { text = "Привет!" } }
Этот блок кода обрабатывает новые сессии. Если сессия новая message.session.new
, то в ответ отправляется текст «Привет!».
message { response { text = message.request.command.toString() } }
Этот блок кода обрабатывает все остальные сообщения. В ответ отправляется текст запроса пользователя.
Мидлвари
Мидлварь — это код, который активируется при каждом событии, полученном от API Алисы.
Есть два типа Мидлвари
-
Внешняя (outer) область — вызывается перед обработкой фильтров (
innerMiddleware
). -
Внутренняя (inner) область — вызывается после обработки фильтров, но перед обработчиком (
outerMiddleware
).
Мидлварь должен всегда возвращать null чтобы передать событие следующему мидлварю/хэндлеру. Если вы хотите завершить обработку события, вы должны вернуть
Response
.
Пример
dispatch { // ... outerMiddleware { if(message.session.user?.userId == null) response { text = "У вас нет аккаунта в Яндексе." } else null } // ... }
Обработка исключений
responseFailure — это расширение для Dispatcher, которое позволяет обрабатывать ошибки, возникающие при выполнении запросов. Оно предоставляет возможность задать обработчики ошибок для различных типов исключений и условий.
responseFailure
должен всегда возвращать null чтобы передать событие следующему хэндлеру. Если вы хотите завершить обработку события, вы должны вернутьResponse
.
Пример
-
Обработка конкретного исключения.
responseFailure(ArithmeticException::class) { response { text = "Произошла арифметическая ошибка" } }
-
Общий обработчик исключений.
responseFailure { response { text = "Произошла ошибка" } }
-
Обработка ошибок по условию.
responseFailure({ message.session.new }) { response { text = "В начале сессии произошла ошибка" } }
Dialog API
Чтобы получить, загрузить и удалить загруженные изображения и звуки, надо передать OAuth Token при создании DialogApi
.
skill { // ... dialogApi = ktorYandexDialogApi { oauthToken = "..." } // ... }.run()
Для каждого аккаунта Яндекса на Диалоги можно загрузить не больше 100 МБ
картинок и 1 ГБ
аудио. Чтобы узнать, сколько места уже занято, используйте этот метод.
dialogApi.getStatus()
Все доступные методы API.
interface DialogApi { suspend fun getStatus(): Response<Status> suspend fun uploadImage(url: String): Response<ImageUpload> suspend fun uploadImage(file: File): Response<ImageUpload> suspend fun getAllImages(): Response<Images> suspend fun deleteImage(id: String): Response<Unit> suspend fun uploadSound(file: File): Response<SoundUpload> suspend fun getAllSounds(): Response<Sounds> suspend fun deleteSound(id: String): Response<Unit> }
Все методы API возвращают обёртку Response<>
.
sealed interface Response<T> { data class Failed<T>(val message: String): Response<T> data class Success<T>(val data: T): Response<T> }
Машина состояний
Не вся функциональность навыка может быть реализована в одном хэндлере. Если вам нужно получить некоторую информацию от пользователя в несколько шагов или нужно направить его в зависимости от ответа, то вам надо использовать FSM.
Для начала определите возможные состояния в вашем навыке
enum class InfoState { SET_NAME, SET_AGE, SET_INFO }
Начальное Состояние, когда начинается новая сессия, установите начальное состояние
message({ message.session.new }) { state.setState(InfoState.SET_NAME.name) response { text = "Добро пожаловать в навык, как вас зовут?" } }
-
Условие
: Обрабатывается при начале новой сессии. -
Действие
: Устанавливает состояние вSET_NAME
и запрашивает у пользователя имя.
После получения имени от пользователя, сохраняем его и переходим к следующему состоянию.
message({ state == InfoState.SET_NAME.name }) { val username = message.request.originalUtterance.toString() state.updateData("name" to username) state.setState(InfoState.SET_AGE.name) response { text = "Рад познакомиться $username, сколько вам лет?" } }
-
Условие
: Обрабатывается, если текущее состояниеSET_NAME
. -
Действие
: Сохраняет имя в состоянии, устанавливает следующее состояниеSET_AGE
, и запрашивает возраст.
После получения возраста от пользователя, сохраняем его и переходим к последнему состоянию.
message({ state == InfoState.SET_AGE.name }) { val age = message.request.originalUtterance.toString() state.updateData("age" to age) state.setState(InfoState.SET_INFO.name) response { text = "Супер, расскажите о себе" } }
-
Условие
: Обрабатывается, если текущее состояниеSET_AGE
. -
Действие
: Сохраняет возраст в состоянии, устанавливает следующее состояниеSET_INFO
, и запрашивает дополнительную информацию.
На заключительном этапе, после получения дополнительной информации, формируем окончательный ответ и завершаем сессию.
message({state == InfoState.SET_INFO.name}) { val info = message.request.originalUtterance.toString() val data = state.getData() state.clear() response { text = "Вот что мне удалось узнать\n\nИмя-${data["name"]}\nВозраст-${data["age"]}\nИнформация-$info" endSession = true } }
-
Условие
: Обрабатывается, если текущее состояниеSET_INFO
. -
Действие
: Формирует текст ответа на основе собранной информации, очищает состояние и завершает сессию.Использование машины состояний позволяет структурировать взаимодействие с пользователем и управлять процессом сбора информации через последовательные шаги. Это особенно полезно для сложных сценариев, требующих многократного взаимодействия и сохранения состояния между шагами.
Кнопки и альбомы
Для того чтобы добавить кнопку используйте метод
button
.
response { text = "Выберите тип" SchedulesType.entries.forEach { button { text = it.name payload = mapOf("schedule_type" to it.name) } } }
Существует три типа альбомов
-
Card Items List.
response { text = "CARD ITEMS LIST" cardItemsList { header = "HEADER" repeat(10) { index -> item { imageId = IMAGE_ID title = "#${index + 1}" } } footer { text = "Footer text" mediaButton { text = "Click" } } } }
-
Card Big Image.
response { cardBigImage { imageId = IMAGE_ID title = "CARD BIG IMAGE" mediaButton { text = "Open url" url = "https://ya.ru" } } }
-
Card Image Gallery.
response { cardImageGallery { repeat(10) { index -> item { imageId = IMAGE_ID title = "#${index + 1}" } } } }
Полезные ссылки
ссылка на оригинал статьи https://habr.com/ru/articles/834264/
Добавить комментарий