プッシュ通知(Dialogflow)

Dialogflow で調べる

[続行] をクリックして、Dialogflow に Notifications のサンプルをインポートします。次に、以下の手順に沿ってサンプルをデプロイしてテストします。

  1. エージェント名を入力し、サンプル用に新しい Dialogflow エージェントを作成します。
  2. エージェントのインポートが完了したら、[Go to agent] をクリックします。
  3. メインのナビゲーション メニューから [Fulfillment] に移動します。
  4. [インライン エディタ] を有効にして、[デプロイ] をクリックします。エディタにはサンプルコードが含まれています。
  5. メインのナビゲーション メニューから [Integrations] に移動し、[Google Assistant] をクリックします。
  6. 表示されたモーダル ウィンドウで [変更の自動プレビュー] を有効にし、[Test] をクリックして Actions シミュレータを開きます。
  7. シミュレータで「Talk to my test app」と入力してサンプルをテストします。
続行

タスクの期限が近づいたときにリマインダーを送信するなど、必要に応じてアクションからユーザーに通知をプッシュできます。

このガイドでは、アクションのプッシュ通知を設定する方法を示すため、Actions on Google のヒントサンプルを使用します。このアクションを呼び出すと、独自のアクションの開発に関するヒントを聞くかどうかを尋ねるメッセージが表示されます。ユーザーはヒントの特定のカテゴリまたはランダムに選択されたカテゴリを選ぶことができ、最新のヒントを聞くこともできます。

サポート対象のサーフェス

プッシュ通知は、Android デバイスと iOS デバイスで利用できます(iOS デバイスにプッシュ通知を受信するには、アシスタント アプリをインストールする必要があります)。現在のところ、音声認識スピーカー、スマートディスプレイ、その他のサーフェスではサポートされていません。

前提条件

ユーザーがアシスタントから受け取った通知をタップすると呼び出されるトリガー インテントとして、アクション プロジェクト内の 1 つ以上のアクションを設定しておく必要があります。

プッシュ通知からデフォルト ウェルカム インテントをトリガーするようにアクションを設定することはできません。

コンソールの設定

アクションにプッシュ通知のサポートを追加するには:

  1. Actions Console で、[Build] > [Actions] に移動します。

  2. プッシュ通知を有効にする追加のトリガー インテントに一致するアクションをクリックします。

    Actions on Google のヒントサンプルでは、「tell_latest_tip」を選択します。

  3. [User engagement] セクションで [Would you like to send push notifications] をオンにします。

  4. コンテンツのタイトルを入力します。

    Actions on Google のヒントサンプルでは、タイトルは「New tip added」にします。

  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 Java
responseBuilder
    .add("I can send you push notifications. Would you like that?")
    .addSuggestions(new String[] {
        "Send notifications"
    });
Actions SDK Java
responseBuilder
    .add("I can send you push notifications. Would you like that?")
    .addSuggestions(new String[] {
        "Send notifications"
    });
Dialogflow JSON

下記の JSON は Webhook レスポンスを示します。

{
  "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 は Webhook レスポンスを示します。

{
  "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. [Response] で [Google Assistant] タブを開きます。
  3. [Add message content] をクリックして [Suggestion chips] を選択します。
  4. 候補ワードのテキストを、ユーザーにオプトインを促す文言に設定します。Actions on Google のヒントサンプルでは、チップを [Alert me of new resources] に設定します。
  5. 別の Dialogflow インテント(setup_pushsetup_push など)を追加し、対応するアクション(setup.pushsetup_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 Java
  1. Dialogflow コンソールでエージェントを開き、更新用に構成するインテントを選択します。
  2. [Response] で [Google Assistant] タブを開きます。
  3. [Add message content] をクリックして [Suggestion chips] を選択します。
  4. 候補ワードのテキストを、ユーザーにオプトインを促す文言に設定します。Actions on Google のヒントサンプルでは、チップを [Alert me of new resources] に設定します。
  5. 別の Dialogflow インテント(setup_pushsetup_push など)を追加し、対応するアクション(setup.pushsetup_push など)を設定します。このインテントのユーザー表現は、オプトイン チップのテキスト(この例では「新しいヒントのアラート」の例)と一致する必要があります。
次のスニペットは、Actions on Google Java/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 Java

ユーザー表現がプッシュ通知のオプトイン プロンプトの値と一致する場合に許可を求める関数をトリガーするように NLU ソリューションを構成する必要があります。文字列のマッチングに基づく基本的な例を以下に示します。

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

以下の JSON は、Dialogflow を使用した Webhook レスポンスを示します。

{
  "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 を使用した Webhook レスポンスを示します。

{
  "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 Webhook からサブスクリプションを完了するには、ユーザーの通知 ID とユーザーが選択したインテントを保存する必要があります。ユーザーが権限を付与した場合、どちらも引数として渡されます。

Dialogflow でアクションを作成している場合は、次のことを行う必要があります。

  • actions_intent_PERMISSION を処理するインテントを追加します。
  • インテントのアクション名を、後で Webhook によってフィルタリングできる名前に設定します。

次のコードは、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 Java
@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 Java
@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 は Webhook へのリクエストを示します。

{
  "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 は Webhook へのリクエストを示します。

{
  "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」は、Actions Console のプロジェクト ID に置き換えてください。https://console.developers.google.com/apis/api/actions.googleapis.com/overview?project=example-project-1
  2. [有効にする] ボタンが表示されている場合はクリックします。表示されていない場合は、手順 3 に進みます。
  3. 次の URL にアクセスします。最後の「example-project-1」は、Actions Console のプロジェクト ID に置き換えてください。https://console.developers.google.com/apis/credentials?project=example-project-1
  4. [認証情報を作成] > [サービス アカウント キー] をクリックします。
  5. [サービス アカウント] の下にある [選択] ボックスをクリックし、[新しいサービス アカウント] をクリックします。
  6. サービス アカウントに「notifications」などの名前を付け、プロジェクト オーナーロールを設定します。
  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 Java
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 Java
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);
}