Исследуйте в Dialogflow
Нажмите «Продолжить» , чтобы импортировать наш образец уведомлений в Dialogflow. Затем выполните следующие действия, чтобы развернуть и протестировать образец:
- Введите имя агента и создайте новый агент Dialogflow для примера.
- После завершения импорта агента нажмите Перейти к агенту .
- В главном навигационном меню перейдите в раздел «Выполнение» .
- Включите встроенный редактор и нажмите «Развернуть» . Редактор содержит пример кода.
- В главном меню навигации выберите «Интеграции» и нажмите «Google Ассистент» .
- В появившемся модальном окне включите автоматический просмотр изменений и нажмите «Тест» , чтобы открыть симулятор действий.
- В симуляторе введите
Talk to my test app
чтобы протестировать образец!
Ваше действие может отправлять пользователям уведомления, когда это необходимо, например отправлять напоминание о приближении срока выполнения задачи.
В этом руководстве мы используем образец советов Actions on Google в качестве справочного материала, чтобы показать вам, как настроить push-уведомления для вашего действия. Когда пользователи вызывают это действие, оно спрашивает, хотят ли они услышать совет о разработке собственного действия. Пользователи могут выбрать конкретную или случайно выбранную категорию для подсказки или прослушать самую последнюю подсказку.
Поддерживаемые поверхности
Push-уведомления доступны на устройствах Android и iOS (для получения push-уведомлений на устройствах iOS должно быть установлено приложение Assistant). В настоящее время они не поддерживаются на колонках с голосовым управлением, интеллектуальных дисплеях и других поверхностях.
Предварительные условия
По крайней мере одно из действий в вашем проекте действий должно быть настроено как триггерное намерение, которое будет вызываться, когда пользователь нажимает на уведомление, полученное от Ассистента.
Ваши действия не могут быть настроены на запуск приветственного намерения по умолчанию из push-уведомления.
Настройка консоли
Чтобы добавить поддержку push-уведомлений в действие:
Перейдите в консоль Actions и выберите Build > Actions .
Щелкните действие, соответствующее дополнительному триггерному намерению, для которого вы хотите включить push-уведомления.
Для примера советов Actions on Google выберите «tell_latest_tip».
Прокрутите вниз до раздела «Вовлеченность пользователей» и включите параметр «Хотите ли вы отправлять push-уведомления?» .
Введите заголовок контента .
В примере с советами Actions on Google заголовок может быть «Добавлен новый совет».
Нажмите Сохранить .
Импорт
Для целей следующих разделов в коде выполнения вам необходимо будет объявить следующий импорт:
const { dialogflow, UpdatePermission, Suggestions, } = require('actions-on-google');
const { actionssdk, UpdatePermission, Suggestions, } = require('actions-on-google');
Подписавшиеся пользователи
Прежде чем вы сможете отправлять push-уведомления пользователям, вы должны попросить их дать свое согласие. Вы можете сделать это, показав им чип с предложением и спросив их разрешения. Когда они предоставляют разрешение, вы получаете обновленный идентификатор пользователя для отправки push-уведомлений этому пользователю.
Показывать чипы предложений для согласия
Прежде чем пользователи смогут получать push-уведомления от вашего действия, вы должны показать им чип предложения, чтобы предложить им подписаться на push-уведомления.
Следующий фрагмент кода отправляет пользователю чип предложения «Оповещать меня о новых советах» вместе с текстовым ответом.
conv.ask('I can send you push notifications. Would you like that?'); conv.ask(new Suggestions('Send notifications'));
conv.ask(' I can send you push notifications. Would you like that?'); conv.ask(new Suggestions('Send notifications'));
responseBuilder .add("I can send you push notifications. Would you like that?") .addSuggestions(new String[] { "Send notifications" });
responseBuilder .add("I can send you push notifications. Would you like that?") .addSuggestions(new String[] { "Send notifications" });
Обратите внимание, что приведенный ниже JSON описывает ответ веб-перехватчика.
{ "payload": { "google": { "expectUserResponse": true, "richResponse": { "items": [ { "simpleResponse": { "textToSpeech": "Hi! Welcome to Push Notifications!" } }, { "simpleResponse": { "textToSpeech": "I can send you push notifications. Would you like that?" } } ], "suggestions": [ { "title": "Send notifications" } ] } } } }
Обратите внимание, что приведенный ниже JSON описывает ответ веб-перехватчика.
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.TEXT" } ], "inputPrompt": { "richInitialPrompt": { "items": [ { "simpleResponse": { "textToSpeech": "Hi! Welcome to Push Notifications!" } }, { "simpleResponse": { "textToSpeech": " I can send you push notifications. Would you like that?" } } ], "suggestions": [ { "title": "Send notifications" } ] } } } ] }
После того, как они коснутся чипа, вы должны запросить разрешение UPDATE
. Следующий код показывает, как это сделать с помощью функции askForUpdatePermission
клиентской библиотеки Node.js.
- Откройте свой агент в консоли Dialogflow и выберите намерение, которое вы настраиваете для обновлений.
- Прокрутите вниз до раздела «Ответ» и откройте вкладку Google Assistant .
- Нажмите «Добавить содержимое сообщения» и выберите «Чипы предложений» .
- Установите в тексте чипа что-то, что приглашает пользователя согласиться. В примере советов Actions on Google мы установили функцию « Оповещать меня о новых советах» .
- Добавьте еще одно намерение Dialogflow, например, setup_push , и установите соответствующее действие, например setup.push . Пользовательское выражение этого намерения должно совпадать с текстом чипа согласия, в нашем примере «Оповещать меня о новых советах» .
app.intent('Subscribe to Notifications', (conv) => { conv.ask(new UpdatePermission({ intent: 'Notification', })); });
Вам следует настроить решение NLU так, чтобы оно запускало функцию, которая запрашивает разрешение, если пользовательское выражение соответствует значению запроса на получение push-уведомлений. Вот очень простой пример, основанный на сопоставлении строк:
conv.ask(new UpdatePermission({ intent: 'Notification', }));
- Откройте свой агент в консоли Dialogflow и выберите намерение, которое вы настраиваете для обновлений.
- Прокрутите вниз до раздела «Ответ» и откройте вкладку Google Assistant .
- Нажмите «Добавить содержимое сообщения» и выберите «Чипы предложений» .
- Установите в тексте чипа что-то, что приглашает пользователя согласиться. В примере советов Actions on Google мы установили функцию « Оповещать меня о новых советах» .
- Добавьте еще одно намерение Dialogflow, например, setup_push , и установите соответствующее действие, например setup.push . Пользовательское выражение этого намерения должно совпадать с текстом чипа согласия, в нашем примере «Оповещать меня о новых советах» .
@ForIntent("Subscribe to Notifications") public ActionResponse subscribeToNotifications(ActionRequest request) { ResponseBuilder responseBuilder = getResponseBuilder(request); responseBuilder.add(new UpdatePermission().setIntent("Notification")); return responseBuilder.build(); }
Вам следует настроить решение NLU так, чтобы оно запускало функцию, которая запрашивает разрешение, если пользовательское выражение соответствует значению запроса на получение push-уведомлений. Вот очень простой пример, основанный на сопоставлении строк:
ResponseBuilder responseBuilder = getResponseBuilder(request); responseBuilder.add(new UpdatePermission().setIntent("Notification")); return responseBuilder.build();
Обратите внимание, что приведенный ниже JSON описывает ответ веб-перехватчика с использованием Dialogflow.
{ "payload": { "google": { "expectUserResponse": true, "systemIntent": { "intent": "actions.intent.PERMISSION", "data": { "@type": "type.googleapis.com/google.actions.v2.PermissionValueSpec", "permissions": [ "UPDATE" ], "updatePermissionValueSpec": { "intent": "tell_latest_tip" } } } } } }
Обратите внимание, что приведенный ниже JSON описывает ответ веб-перехватчика с использованием Actions SDK.
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.PERMISSION", "inputValueData": { "@type": "type.googleapis.com/google.actions.v2.PermissionValueSpec", "permissions": [ "UPDATE" ], "updatePermissionValueSpec": { "intent": "tell_latest_tip" } } } ] } ] }
Завершить подписку
Чтобы завершить подписку с помощью веб-перехватчика Node.js, вам необходимо сохранить идентификатор уведомлений пользователя и выбранное им намерение. Оба передаются в качестве аргументов, если пользователь предоставляет разрешение.
Если ваше действие создано с помощью Dialogflow, вам необходимо:
- Добавьте намерение, которое обрабатывает
actions_intent_PERMISSION
. - Укажите имя действия намерения, которое веб-перехватчик сможет фильтровать позже.
Следующий код показывает, как обрабатывать намерение Dialogflow с намерением finish_push_setup
и именем действия finish.push.setup
:
app.intent('Confirm Notifications Subscription', (conv) => { if (conv.arguments.get('PERMISSION')) { const updatesUserId = conv.arguments.get('UPDATES_USER_ID'); // Store user ID in database for later use conv.close(`Ok, I'll start alerting you.`); } else { conv.close(`Ok, I won't alert you.`); } });
app.intent('actions.intent.PERMISSION', (conv) => { if (conv.arguments.get('PERMISSION')) { const updatesUserId = conv.arguments.get('UPDATES_USER_ID'); // Store user ID in database for later use conv.close(`Ok, I'll start alerting you.`); } else { conv.close(`Ok, I won't alert you.`); } });
@ForIntent("Confirm Notifications Subscription") public ActionResponse confirmNotificationsSubscription(ActionRequest request) { // Verify the user has subscribed for push notifications ResponseBuilder responseBuilder = getResponseBuilder(request); if (request.isPermissionGranted()) { Argument userId = request.getArgument(ConstantsKt.ARG_UPDATES_USER_ID); if (userId != null) { // Store the user's ID in the database } responseBuilder.add("Ok, I'll start alerting you."); } else { responseBuilder.add("Ok, I won't alert you."); } responseBuilder.endConversation(); return responseBuilder.build(); }
@ForIntent("actions.intent.PERMISSION") public ActionResponse confirmNotificationsSubscription(ActionRequest request) { // Verify the user has subscribed for push notifications ResponseBuilder responseBuilder = getResponseBuilder(request); if (request.isPermissionGranted()) { Argument userId = request.getArgument(ConstantsKt.ARG_UPDATES_USER_ID); if (userId != null) { // Store the user's ID in the database } responseBuilder.add("Ok, I'll start alerting you."); } else { responseBuilder.add("Ok, I won't alert you."); } responseBuilder.endConversation(); return responseBuilder.build(); }
Обратите внимание, что приведенный ниже JSON описывает запрос к веб-перехватчику.
{ "responseId": "ee9e7ed5-fa1a-48c6-aac7-f9fbe94f1f58-712767ed", "queryResult": { "queryText": "actions_intent_PERMISSION", "action": "confirm.subscription", "parameters": {}, "allRequiredParamsPresent": true, "fulfillmentMessages": [ { "text": { "text": [ "" ] } } ], "outputContexts": [ { "name": "projects/PROJECT_ID/agent/sessions/ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k/contexts/actions_capability_screen_output" }, { "name": "projects/PROJECT_ID/agent/sessions/ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k/contexts/actions_capability_account_linking" }, { "name": "projects/PROJECT_ID/agent/sessions/ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k/contexts/actions_capability_media_response_audio" }, { "name": "projects/PROJECT_ID/agent/sessions/ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k/contexts/actions_capability_audio_output" }, { "name": "projects/PROJECT_ID/agent/sessions/ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k/contexts/actions_capability_web_browser" }, { "name": "projects/PROJECT_ID/agent/sessions/ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k/contexts/google_assistant_input_type_keyboard" }, { "name": "projects/PROJECT_ID/agent/sessions/ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k/contexts/actions_intent_permission", "parameters": { "PERMISSION": true, "text": "yes", "UPDATES_USER_ID": "ABwppHHssyPbvEBF1mgN7Ddwb7mkhiVohW9PZ--I_svqy7zFElA4DHkf9pn04UBd5gwZo26_RfXCQ8otcztyIfe6MCQ" } } ], "intent": { "name": "projects/PROJECT_ID/agent/intents/c7f7b30b-5b88-4bb5-b0b8-1cd0862d1dd2", "displayName": "Confirm Notifications Subscription" }, "intentDetectionConfidence": 1, "languageCode": "en" }, "originalDetectIntentRequest": { "source": "google", "version": "2", "payload": { "user": { "permissions": [ "UPDATE" ], "locale": "en-US", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k", "type": "ACTIVE", "conversationToken": "[]" }, "inputs": [ { "intent": "actions.intent.PERMISSION", "rawInputs": [ { "inputType": "KEYBOARD", "query": "yes" } ], "arguments": [ { "name": "PERMISSION", "boolValue": true, "textValue": "true" }, { "name": "text", "rawText": "yes", "textValue": "yes" }, { "name": "UPDATES_USER_ID", "textValue": "ABwppHHssyPbvEBF1mgN7Ddwb7mkhiVohW9PZ--I_svqy7zFElA4DHkf9pn04UBd5gwZo26_RfXCQ8otcztyIfe6MCQ" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" } ] } ] } }, "session": "projects/PROJECT_ID/agent/sessions/ABwppHGIgmmU3zBcYMF_vWoHaM4JUo3wniYBHdbUF25l63G7EQWjRnlne8Ar7AOcRHWn1lrEKGy8qdP0UXLcWDBq93k" }
Обратите внимание, что приведенный ниже JSON описывает запрос к веб-перехватчику.
{ "user": { "permissions": [ "UPDATE" ], "locale": "en-US", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHEP6OAFZHkSGEiZ5HYM9qrlk8YtIH1DQmJ52cxXELSPvM-kSc_tMJ_5O6ITbgVJlY9i2FIsKWjE_HXLke48", "type": "NEW" }, "inputs": [ { "intent": "actions.intent.PERMISSION", "rawInputs": [ { "inputType": "KEYBOARD", "query": "yes" } ], "arguments": [ { "name": "PERMISSION", "boolValue": true, "textValue": "true" }, { "name": "text", "rawText": "yes", "textValue": "yes" }, { "name": "UPDATES_USER_ID", "textValue": "ABwppHFvBKC-tMYUsUjJkm3YECgZvd6A3sOc7KuQvO4ZdQX3bGLmyoQ41dh4Zmtlzv_kaOKBt1Sf6eRpNbayynrl" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" } ] } ] }
Отправлять уведомления
Вы можете отправлять push-уведомления пользователям с помощью Actions API. Чтобы использовать этот API, вам необходимо активировать API в своем проекте Google Cloud, а также настроить и загрузить ключ учетной записи службы JSON. См. шаг №8 в инструкциях в примере кода здесь .
Затем вы можете использовать клиентскую библиотеку Google OAuth2 для обмена ключа учетной записи службы на токен доступа и использовать токен для аутентификации ваших запросов к API действий.
Получить ключ сервисного аккаунта
- Перейдите по этому URL-адресу, заменив «example-project-1» в конце идентификатором вашего проекта в консоли действий: https://console.developers.google.com/apis/api/actions.googleapis.com/overview?project =пример-проект-1
- Если вы видите кнопку «Включить» , нажмите ее. В противном случае перейдите к шагу 3.
- Перейдите по этому URL-адресу, заменив «example-project-1» в конце идентификатором вашего проекта в консоли действий: https://console.developers.google.com/apis/credentials?project=example-project-1.
- Нажмите «Создать учетные данные» > «Ключ учетной записи службы» .
- Щелкните поле «Выбрать» в разделе «Учетная запись службы» и нажмите «Новая учетная запись службы» .
- Присвойте учетной записи службы имя, например «уведомления», и роль владельца проекта .
- Выберите тип ключа JSON и нажмите «Создать» . Ключ учетной записи службы JSON загружается на ваш локальный компьютер.
Обменяйте ключ на токен доступа и отправьте уведомление.
Чтобы отправить уведомление через API действий, вам необходимо обменять ключ учетной записи службы на токен доступа. Мы рекомендуем использовать для этого клиентскую библиотеку Google API. В следующей серии фрагментов кода мы используем клиентскую библиотеку Google API Node.js.
- Установите клиентскую библиотеку Google API и запросите:
npm install googleapis request --save
- Используйте следующий код, чтобы получить токен доступа из ключа учетной записи службы и отправить push-уведомление:
const {google} = require('googleapis'); const request = require('request'); const jwtClient = new google.auth.JWT( serviceAccount.client_email, null, serviceAccount.private_key, ['https://www.googleapis.com/auth/actions.fulfillment.conversation'], null ); jwtClient.authorize((err, tokens) => { if (!err) { request.post('https://actions.googleapis.com/v2/conversations:send', { auth: { bearer: tokens.access_token, }, json: true, body: { customPushMessage: { userNotification: { title: 'Push Notification Title', }, target: { userId: '<UPDATES_USER_ID>', intent: 'Notification Intent', }, }, isInSandbox: true, }, }, (err, httpResponse, body) => { console.log(`${httpResponse.statusCode}: ${httpResponse.statusMessage}`); }); } });
const {google} = require('googleapis'); const request = require('request'); const jwtClient = new google.auth.JWT( serviceAccount.client_email, null, serviceAccount.private_key, ['https://www.googleapis.com/auth/actions.fulfillment.conversation'], null ); jwtClient.authorize((err, tokens) => { if (!err) { request.post('https://actions.googleapis.com/v2/conversations:send', { auth: { bearer: tokens.access_token, }, json: true, body: { customPushMessage: { userNotification: { title: 'Push Notification Title', }, target: { userId: '<UPDATES_ORDER_ID>', intent: 'Notification Intent', }, }, isInSandbox: true, }, }, (err, httpResponse, body) => { console.log(`${httpResponse.statusCode}: ${httpResponse.statusMessage}`); }); } });
final class Notification { private final String title; Notification(String title) { this.title = title; } String getTitle() { return title; } } final class Target { private final String userId; private final String intent; private final String locale; Target(String userId, String intent, String locale) { this.userId = userId; this.intent = intent; this.locale = locale; } String getUserId() { return userId; } String getIntent() { return intent; } String getLocale() { return locale; } } final class PushMessage { private final Notification userNotification; private final Target target; PushMessage(Notification userNotification, Target target) { this.userNotification = userNotification; this.target = target; } Notification getUserNotification() { return userNotification; } Target getTarget() { return target; } } final class PushNotification { private final PushMessage customPushMessage; private boolean isInSandbox; PushNotification(PushMessage customPushMessage, boolean isInSandbox) { this.customPushMessage = customPushMessage; this.isInSandbox = isInSandbox; } PushMessage getCustomPushMessage() { return customPushMessage; } boolean getIsInSandbox() { return isInSandbox; } } private PushNotification createNotification(String title, String userId, String intent, String locale) { Notification notification = new Notification(title); Target target = new Target(userId, intent, locale); PushMessage message = new PushMessage(notification, target); boolean isInSandbox = true; return new PushNotification(message, isInSandbox); } private ServiceAccountCredentials loadCredentials() throws IOException { String actionsApiServiceAccountFile = this.getClass().getClassLoader().getResource("service-account.json").getFile(); InputStream actionsApiServiceAccount = new FileInputStream(actionsApiServiceAccountFile); ServiceAccountCredentials serviceAccountCredentials = ServiceAccountCredentials.fromStream(actionsApiServiceAccount); return (ServiceAccountCredentials) serviceAccountCredentials.createScoped( Collections.singleton( "https://www.googleapis.com/auth/actions.fulfillment.conversation")); } private String getAccessToken() throws IOException { AccessToken token = loadCredentials().refreshAccessToken(); return token.getTokenValue(); } public void sendNotification(String title, String userId, String intent, String locale) throws IOException { Preconditions.checkNotNull(title, "title cannot be null."); Preconditions.checkNotNull(userId, "userId cannot be null."); Preconditions.checkNotNull(intent, "intent cannot be null."); Preconditions.checkNotNull(locale, "locale cannot be null"); PushNotification notification = createNotification(title, userId, intent, locale); HttpPost request = new HttpPost("https://actions.googleapis.com/v2/conversations:send"); String token = getAccessToken(); request.setHeader("Content-type", "application/json"); request.setHeader("Authorization", "Bearer " + token); StringEntity entity = new StringEntity(new Gson().toJson(notification)); entity.setContentType(ContentType.APPLICATION_JSON.getMimeType()); request.setEntity(entity); HttpClient httpClient = HttpClientBuilder.create().build(); httpClient.execute(request); }
final class Notification { private final String title; Notification(String title) { this.title = title; } String getTitle() { return title; } } final class Target { private final String userId; private final String intent; Target(String userId, String intent) { this.userId = userId; this.intent = intent; } String getUserId() { return userId; } String getIntent() { return intent; } } final class PushMessage { private final Notification userNotification; private final Target target; PushMessage(Notification userNotification, Target target) { this.userNotification = userNotification; this.target = target; } Notification getUserNotification() { return userNotification; } Target getTarget() { return target; } } final class PushNotification { private final PushMessage customPushMessage; private boolean isInSandbox; PushNotification(PushMessage customPushMessage, boolean isInSandbox) { this.customPushMessage = customPushMessage; this.isInSandbox = isInSandbox; } PushMessage getCustomPushMessage() { return customPushMessage; } boolean getIsInSandbox() { return isInSandbox; } } private PushNotification createNotification(String title, String userId, String intent) { Notification notification = new Notification(title); Target target = new Target(userId, intent); PushMessage message = new PushMessage(notification, target); boolean isInSandbox = true; return new PushNotification(message, isInSandbox); } private ServiceAccountCredentials loadCredentials() throws IOException { String actionsApiServiceAccountFile = this.getClass().getClassLoader().getResource("service-account.json").getFile(); InputStream actionsApiServiceAccount = new FileInputStream(actionsApiServiceAccountFile); ServiceAccountCredentials serviceAccountCredentials = ServiceAccountCredentials.fromStream(actionsApiServiceAccount); return (ServiceAccountCredentials) serviceAccountCredentials.createScoped( Collections.singleton( "https://www.googleapis.com/auth/actions.fulfillment.conversation")); } private String getAccessToken() throws IOException { AccessToken token = loadCredentials().refreshAccessToken(); return token.getTokenValue(); } public void sendNotification(String title, String userId, String intent) throws IOException { Preconditions.checkNotNull(title, "title cannot be null."); Preconditions.checkNotNull(userId, "userId cannot be null."); Preconditions.checkNotNull(intent, "intent cannot be null."); PushNotification notification = createNotification(title, userId, intent); HttpPost request = new HttpPost("https://actions.googleapis.com/v2/conversations:send"); String token = getAccessToken(); request.setHeader("Content-type", "application/json"); request.setHeader("Authorization", "Bearer " + token); StringEntity entity = new StringEntity(new Gson().toJson(notification)); entity.setContentType(ContentType.APPLICATION_JSON.getMimeType()); request.setEntity(entity); HttpClient httpClient = HttpClientBuilder.create().build(); httpClient.execute(request); }