Поиск не вставая с дивана, или как подружить приложение со Сбер Ассистентом

от автора

Всем привет ? . Зовут меня Илюша Кр, и сегодня я вам расскажу, как же все-таки попросить ассистента принести чипсов сделать поисковый запрос внутри вашего приложения.

Для начала скажу пару слов о себе: я такой же обычный парень, как и вы, работаю разработчиком под Android в онлайн-кинотеатре PREMIER. Но, когда я взял задачу по внедрению голосового помощника Сбера в приложение, моя жизнь разделилась на «до» и «после». Заинтриговал? Тогда читай дальше!

В этой статье вы узнаете:

  • Как интегрировать ваше приложение с приставками Сбера и телевизорами;

  • Как обработать запрос пользователя и взять только нужную информацию;

  • ответ на загадку Жака Фреско Как обрабатывать данные от ассистента непосредственно в самом приложении.

Ачо, а всмысле?

Так, давайте определимся с задачей: у Сбер-девайсов на пульте четыре кнопки и они вам не нужны, поскольку есть ГОЛОСОВОЙ ПОМОЩНИК. А это значит, что поиск контента нужно сделать посредством голосового ввода. Если же мы находимся не на экране поиска, откроем этот экран и введем запрос пользователя. Этим мы и займемся!

Подготовка

Начнем с того, что нам необходимо зарегистрировать наше приложение в SberMarket Studio. В личном пространстве нажмите «Создать проект» и выберите пункт SmartApp. В качестве типа смартапа выбираем NativeApp. В настройках проекта добавьте apk-файл. Et voilà, теперь ваше приложение является нэйтив апп и в него можно встроить сберовские примочки.

Само собой все это в доках Сбера есть, но смотреть туда неинтересно. Ведь поэтому вы здесь? Ну что, продолжим!

Для голосового помощника понадобится добавить:

  • Sdk и логику в приложение;

  • Скрипт бэкенда, который будет обрабатывать запросы.

Настройка приложения

Начнем, пожалуй, с нашего приложения.

Для начала необходимо добавить sdk (спойлер: на момент написания статьи в доках указана версия 1.0, но найти ее не получится, поскольку не релизнута. А сегодня в завтрашний день не все могут смотреть… Ну вы поняли). А вообще вот вроде ссылки на актуальные версии.

У себя в проекте я не использовал «жирный сдк» из-за ненадобности всех фич (только messaging и appstate).

dependencies { ...  implementation "ru.sberdevices.smartapp.sdk:appstate:1.0-alpha-2" implementation "ru.sberdevices.smartapp.sdk:messaging:1.0-alpha-2"  } 

Создадим инстансы для стейта приложения и обмена сообщениями:

data class SberAppState( @SerializedName("isOnScreen") val isOnScreen: Boolean ) : Serializable data class SberMessage( @SerializedName("type")@SerializedName("message") val message: String, @SerializedName("type") val type: String ) : Serializable 

Данные мы будет передавать обычным Json-объектом.

SberAppState представляет из себя класс всего с одним полем — isOnScreen. Данный параметр определяет, в каком стейте находится наше приложение(либо непосредственно на экране поиска, либо где-то еще).

SberMessage состоит из двух полей: type и message. Параметр type нужен для определения, в каком стейте сейчас находится приложение, а message — запрос пользователя в виде строки.

Ну что, посмотрим на код:

const val LOCAL_SEARCH_TYPE = "LOCAL_SEARCH" // Приложение на экране поиска const val GLOBAL_SEARCH_TYPE = "GLOBAL_SEARCH" // Приложение НЕ на экране поиска val listener = object : Messaging.Listener {     override fun onMessage(messageId: MessageId, payload: Payload) {         val sberMessage = Gson().fromJson( payload.data,  SberMessage::class.java )          if (sberMessage.type == LOCAL_SEARCH_TYPE) {             // подставим во фрагменте запрос sberMessage.message         }  if (sberMessage.type == GLOBAL_SEARCH) {             // переадресовывем пользователя на экран поиска         }     }      override fun onError(messageId: MessageId, throwable: Throwable) {         // Пришла ошибка: ~~будем плакать~~ обработаем ее     } }  fun updateAppState(isOnSearchScreen: Boolean) {     appStateHolder.setState(Gson().toJson(SberAppState(isOnSearchScreen))) }  

В функции onMessage обработаем сообщение от ассистента, функция updateAppState в моменты перехода на экран поиска приложение обновляет стейт isOnSearchScreen = true и при уходе — isOnSearchScreen = false соответственно.

Это основная механика обработки запросов в приложении.

Настройка бекенда (написание скрипта)

Взаимодействие пользователя и нашего native app происходит через настроенный нами бэкенд, в котором и закладываем логику обработки фраз пользователя.

В СберМаркет Студио в пространстве приложения на выбор можно создать проект Graph либо Code.

Для данной задачи я выбрал тип Code.

Code — это среда разработки смартаппов на языках JavaScript и SmartApp DSL

Рассмотрим проект. Его структура выглядит следующим образом:

  • src

  • test

  • caila_import.json

  • chatbot.yaml

Мы рассмотрим подробно файлы main.sc и utils.js (про остальные можно почитать в доке).

function reply(body, response) { var replyData = { type: "raw", body: body }; response.replies = response.replies || []; response.replies.push(replyData); } function handleRequest(usersRequest, isOnSearchScreen, response) { var appType = null if (isOnSearchScreen) { appType = 'LOCAL_SEARCH' } else { appType = 'GLOBAL_SEARCH' } var body = {     items: [         {             command: {                 type: 'smart_app_data',                 smart_app_data: {                      type: appType,                      message: usersRequest,                 },             },         },     ], };   reply(body, response);  } 

Функция reply просто отправляет сообщение в наш native app с данными body.

Функция handleRequest получает на вход три параметра:

  • usersRequest — то, что мы передадим в поле message;

  • isOnSearchScreen — параметр, необходимый для разделения логики обработки сообщения в приложении (LOCAL_SEARCH — мы на экране поиска, GLOBAL_SEARCH — мы НЕ на экране поиска);

  • response — параметр, через который мы отправляем сообщение в native app.

theme: / state: runApp     q!: $regex</start>     script:         $response.replies = $response.replies || [];             $response.replies.push({               type: 'raw',                 body: {                     items: [                         {                             command: {                                 type: "smart_app_data",                                 smart_app_data: {                                     type: "app_action",                                     message: "запустиприложение"                                 },                             },                             auto_listening: true                         },                     ],                 },             });    state: UsersSearchRequest     intent!: /UsersRequest     script:         $session.request = $parseTree.text         script: handleRequest($session.request, $jsapi.context().request.rawRequest.payload.meta.current_app.state.isOnScreen, $response)  Need to understand if this state is needed # Если мы не распознали запрос: state: Fallback     event!: noMatch     a: Упс, что то пошло не так :(  

Данный файл выступает навигационным — каждый стейт отвечает за свое действие:

  • state runApp — обрабатывает команды открытия и загрузки приложения на устройство;

  • state UsersSearchRequest — стейт, в который попадает каждый запрос (кроме запуска приложения);

  • state Fallback — для необработанных запросов(которых не должно быть).

Стейт UsersSearchRequest использует в качестве точки входа интент, для которого тренировочная фраза — регулярное выражение: «+» (любое количество символов не меньше одного). При попадании в этот стейт мы должны отправить в приложение данные с помощью функции handleRequest (в секции скрипта мы просто сохраняем в переменную 

Тестирование и отладка сценария

Для того, чтобы произвести рефактор сценария, необходимо быть участником пространства, в котором находится приложение.

Для отладки и тестирования сценария необходимо авторизоваться на Сбер-девайсе пользователем, добавленным в пространство тестируемого приложения.

А как быть, если пространство уже есть, а вас там нет? Все просто: просим уважаемого человека с доступом добавить вас на проект. Для этого, находясь в нужном пространстве, нажимаем на красивую иконку шестеренки в правом верхнем углу, далее вкладка «Команда». На этом экране вы можете добавить нового участника, поменять роли у текущих и, само собой, удалить тех, кто сомневается в вашем идеальном коде.

Заключение

Вот и подошло к концу наше увлекательное путешествие по интеграции помощника. Надеюсь, данная статья будет полезна тем, кто только решил завезти похожий функционал и не знает, с чего начать. Также могу дать совет: при создании Code- и Graph-проектов есть возможность выбрать предустановленный темплейт, попробуйте поковыряться в них — там есть полезный код.

На этом я с вами прощаюсь. Жду ваших вопросов и комментов. Живите долго и процветайте.


ссылка на оригинал статьи https://habr.com/ru/articles/697894/