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

Для начала скажу пару слов о себе: я такой же обычный парень, как и вы, работаю разработчиком под 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 (в секции скрипта мы просто сохраняем в переменную  session.request запрос пользователя). Для извлечения стейта приложения нужно обратиться к данным, переданным вторым параметром.</p><p>Для создания интента перейдем на вкладку Интенты → Создать интент. В разделе «Тренировочные фразы» добавим новую со значением «regexp<.+>».

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

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

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

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

Заключение

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

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

Комментарии (2)


  1. Moskus
    22.11.2022 20:38
    +2

    А что случилось с @dioman648и @arkofom которые тоже писали про Премьер в блоге Газпром-Медиа (в том же стиле с "мемасиками"), которых тоже пригласил @UmaTech Сколько вас там вообще?


  1. Chelidonium
    22.11.2022 21:22

    из сбера в такое шоу, посмотрят и будут приходить с бумажными сберкнижками только