푸시 알림 (Dialogflow)

Dialogflow에서 탐색

계속을 클릭하여 Dialogflow로 알림 샘플을 가져옵니다. 그런 다음 아래 단계에 따라 샘플을 배포하고 테스트합니다.

  1. 에이전트 이름을 입력하고 샘플의 새 Dialogflow 에이전트를 만듭니다.
  2. 에이전트 가져오기가 완료되면 Go to agent(에이전트로 이동)를 클릭합니다.
  3. 기본 탐색 메뉴에서 Fulfillment로 이동합니다.
  4. 인라인 편집기를 사용 설정한 다음 배포를 클릭합니다. 편집기에 샘플 코드가 포함되어 있습니다.
  5. 기본 탐색 메뉴에서 통합으로 이동한 다음 Google 어시스턴트를 클릭합니다.
  6. 표시되는 모달 창에서 Auto-preview changes를 사용 설정하고 Test를 클릭하여 작업 시뮬레이터를 엽니다.
  7. 시뮬레이터에서 Talk to my test app를 입력하여 샘플을 테스트합니다.
계속

작업이 관련성이 있을 때마다 사용자에게 알림을 푸시할 수 있습니다. 예를 들어 작업 마감일이 다가오면 알림을 보낼 수 있습니다.

이 가이드에서는 작업의 푸시 알림을 설정하는 방법을 보여주기 위해 Actions on Google 도움말 샘플을 참조로 사용합니다. 사용자가 이 작업을 호출하면 자체 작업 개발에 관한 도움말을 듣고 싶은지 묻습니다. 사용자는 팁에 대해 특정 카테고리 또는 무작위로 선택된 카테고리를 선택하거나 가장 최신 팁을 듣도록 선택할 수 있습니다.

지원되는 표면

푸시 알림은 Android 및 iOS 기기에서 사용할 수 있습니다. iOS 기기에서 푸시 알림을 받으려면 어시스턴트 앱이 설치되어 있어야 합니다. 현재 음성 활성화 스피커, 스마트 디스플레이 또는 기타 노출 영역에서는 지원되지 않습니다.

기본 요건

작업 프로젝트에서 하나 이상의 작업을 사용자가 어시스턴트에서 수신한 알림을 탭할 때 호출되는 트리거 인텐트로 구성해야 합니다.

푸시 알림에서 기본 시작 인텐트를 트리거하도록 작업을 구성할 수 없습니다.

콘솔 설정

작업에 푸시 알림 지원을 추가하려면 다음 단계를 따르세요.

  1. Actions 콘솔로 이동하여 Build > Actions로 이동합니다.

  2. 푸시 알림을 사용 설정하려는 추가 트리거 인텐트와 일치하는 작업을 클릭합니다.

    Actions on Google 도움말 샘플의 경우 'tell_latest_tip'을 선택합니다.

  3. 사용자 참여 발생 시간 섹션까지 아래로 스크롤하고 푸시 알림을 전송하시겠습니까?를 사용 설정합니다.

  4. 콘텐츠 제목을 입력합니다.

    Actions on Google 도움말 샘플의 경우 제목은 '새로운 도움말 추가됨'일 수 있습니다.

  5. 저장을 클릭합니다.

가져오기

다음 섹션의 목적에 맞게 처리 코드에서 다음과 같은 가져오기를 선언해야 합니다.

Dialogflow
const {
  dialogflow,
  UpdatePermission,
  Suggestions,
} = require('actions-on-google');
Actions SDK
const {
  actionssdk,
  UpdatePermission,
  Suggestions,
} = require('actions-on-google');

선택 사용자

사용자에게 푸시 알림을 보내기 전에 수신 동의를 요청해야 합니다. 부모님께 권한을 요청하는 추천 검색어 칩을 보여주면 됩니다. 사용자가 권한을 부여하면 개발자는 해당 사용자에게 푸시 알림을 보낼 수 있도록 업데이트 사용자 ID를 수신합니다.

선택을 위한 추천 검색어 칩 표시

사용자가 작업에서 푸시 알림을 받으려면 먼저 사용자에게 푸시 알림 수신에 동의하도록 초대하는 추천 칩을 표시해야 합니다.

다음 코드 스니펫은 텍스트 응답과 함께 'Alert me of new tips' 추천 칩을 사용자에게 보냅니다.

Dialogflow Node.js
conv.ask('I can send you push notifications. Would you like that?');
conv.ask(new Suggestions('Send notifications'));
Actions SDK Node.js
conv.ask(' I can send you push notifications. Would you like that?');
conv.ask(new Suggestions('Send notifications'));
Dialogflow 자바
responseBuilder
    .add("I can send you push notifications. Would you like that?")
    .addSuggestions(new String[] {
        "Send notifications"
    });
Actions SDK 자바
responseBuilder
    .add("I can send you push notifications. Would you like that?")
    .addSuggestions(new String[] {
        "Send notifications"
    });
Dialogflow JSON

아래의 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"
          }
        ]
      }
    }
  }
}
Actions SDK JSON

아래의 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 권한을 요청해야 합니다. 다음 코드는 Node.js 클라이언트 라이브러리의 askForUpdatePermission 함수를 사용하여 이 작업을 수행하는 방법을 보여줍니다.

Dialogflow Node.js
  1. Dialogflow 콘솔에서 에이전트를 열고 업데이트를 위해 구성하려는 인텐트를 선택합니다.
  2. 응답까지 아래로 스크롤하고 Google 어시스턴트 탭을 엽니다.
  3. 메시지 콘텐츠 추가를 클릭하고 추천 칩을 선택합니다.
  4. 사용자의 수신 동의를 초대하는 내용으로 칩 텍스트를 설정합니다. Actions on Google 도움말 샘플에서 칩을 Alert me of new tips로 설정합니다.
  5. 예를 들어 setup_push라는 다른 Dialogflow 인텐트를 추가하고 이에 상응하는 작업(예: setup.push)을 설정합니다. 이 인텐트의 사용자 표현은 새 도움말 알림 예에서 수신 동의 칩의 텍스트와 일치해야 합니다.
다음 스니펫은 Node.js용 Actions on Google 클라이언트 라이브러리를 사용하여 권한을 요청하는 방법을 보여줍니다.
app.intent('Subscribe to Notifications', (conv) => {
  conv.ask(new UpdatePermission({
    intent: 'Notification',
  }));
});
Actions SDK Node.js

사용자 표현식이 푸시 알림 선택 메시지 값과 일치하는지 묻는 함수를 트리거하는 함수를 트리거하도록 NLU 솔루션을 구성해야 합니다. 다음은 문자열 일치에 기반한 매우 기본적인 예입니다.

conv.ask(new UpdatePermission({
  intent: 'Notification',
}));
Dialogflow 자바
  1. Dialogflow 콘솔에서 에이전트를 열고 업데이트를 위해 구성하려는 인텐트를 선택합니다.
  2. 응답까지 아래로 스크롤하고 Google 어시스턴트 탭을 엽니다.
  3. 메시지 콘텐츠 추가를 클릭하고 추천 칩을 선택합니다.
  4. 사용자의 수신 동의를 초대하는 내용으로 칩 텍스트를 설정합니다. Actions on Google 도움말 샘플에서 칩을 Alert me of new tips로 설정합니다.
  5. 예를 들어 setup_push라는 다른 Dialogflow 인텐트를 추가하고 이에 상응하는 작업(예: setup.push)을 설정합니다. 이 인텐트의 사용자 표현은 새 도움말 알림 예에서 수신 동의 칩의 텍스트와 일치해야 합니다.
다음 스니펫은 Actions on Google 자바/Kotlin 클라이언트 라이브러리를 사용하여 권한을 요청하는 방법을 보여줍니다.
@ForIntent("Subscribe to Notifications")
public ActionResponse subscribeToNotifications(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  responseBuilder.add(new UpdatePermission().setIntent("Notification"));
  return responseBuilder.build();
}
Actions SDK 자바

사용자 표현식이 푸시 알림 선택 메시지 값과 일치하는지 묻는 함수를 트리거하는 함수를 트리거하도록 NLU 솔루션을 구성해야 합니다. 다음은 문자열 일치에 기반한 매우 기본적인 예입니다.

ResponseBuilder responseBuilder = getResponseBuilder(request);
responseBuilder.add(new UpdatePermission().setIntent("Notification"));
return responseBuilder.build();
Dialogflow JSON

아래 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"
          }
        }
      }
    }
  }
}
Actions SDK JSON

아래 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 웹훅에서 구독을 완료하려면 사용자의 알림 ID와 사용자가 선택한 인텐트를 저장해야 합니다. 사용자가 권한을 부여하면 둘 다 인수로 전달됩니다.

작업이 Dialogflow로 빌드된 경우 다음을 수행해야 합니다.

  • actions_intent_PERMISSION를 처리하는 인텐트를 추가합니다.
  • 인텐트의 Action 이름을 웹훅에서 나중에 필터링할 수 있는 이름으로 지정합니다.

다음 코드는 finish_push_setup라는 인텐트와 작업 이름이 finish.push.setup인 인텐트로 Dialogflow 인텐트를 처리하는 방법을 보여줍니다.

Dialogflow Node.js
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.`);
  }
});
Actions SDK Node.js
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.`);
  }
});
Dialogflow 자바
@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();
}
Actions SDK 자바
@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();
}
Dialogflow JSON

아래의 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"
}
Actions SDK JSON

아래의 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"
        }
      ]
    }
  ]
}

알림 보내기

Actions API를 사용하여 사용자에게 푸시 알림을 보낼 수 있습니다. 이 API를 사용하려면 Google Cloud 프로젝트에서 API를 활성화하고 JSON 서비스 계정 키를 설정하고 다운로드해야 합니다. 여기에 있는 코드 샘플의 안내에서 8단계를 참조하세요.

그런 다음 Google OAuth2 클라이언트 라이브러리를 사용하여 서비스 계정 키를 액세스 토큰으로 교환하고 토큰을 사용하여 Actions API에 대한 요청을 인증할 수 있습니다.

서비스 계정 키 받기

  1. 다음 URL로 이동합니다. 여기서 'example-project-1'을 작업 콘솔의 프로젝트 ID로 바꿉니다. https://console.developers.google.com/apis/api/actions.googleapis.com/overview?project=example-project-1
  2. 사용 설정 버튼이 표시되면 클릭합니다. 그렇지 않으면 3단계로 진행합니다.
  3. 다음 URL로 이동하여 끝부분의 'example-project-1'을 Actions 콘솔의 프로젝트 ID로 바꿉니다. https://console.developers.google.com/apis/credentials?project=example-project-1
  4. 사용자 인증 정보 만들기 > 서비스 계정 키를 클릭합니다.
  5. 서비스 계정에서 선택 상자를 클릭하고 새 서비스 계정을 클릭합니다.
  6. 서비스 계정에 '알림'과 같은 이름 및 프로젝트 소유자역할을 지정합니다.
  7. JSON 키 유형을 선택하고 만들기를 클릭합니다. JSON 서비스 계정 키가 로컬 머신에 다운로드됩니다.

키를 액세스 토큰으로 교환하고 알림 전송

Actions API를 통해 알림을 보내려면 서비스 계정 키를 액세스 토큰으로 교환해야 합니다. 이를 위해 Google API 클라이언트 라이브러리를 사용하는 것이 좋습니다. 이어지는 코드 스니펫 시리즈에서는 Google API Node.js 클라이언트 라이브러리를 사용합니다.

  1. Google API 클라이언트 라이브러리를 설치하고 다음을 요청합니다. npm install googleapis request --save
  2. 다음 코드를 사용하여 서비스 계정 키에서 액세스 토큰을 가져오고 푸시 알림을 전송하세요.
Dialogflow Node.js
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}`);
    });
  }
});
Actions SDK Node.js
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}`);
    });
  }
});
Dialogflow 자바
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);
}
Actions SDK 자바
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);
}