Flutter から Lambda を経由し Amazon Pinpoint のPush通知を実装 ①

# はじめに

flutterでpush通知の実装を目的に、Lambdaを経由してAmazon Pinpointでpush通知する構築方法についてまとめます。


1. pinpointに対して、エンドポイントの登録
![スクリーンショット 2022-06-18 18.11.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/71e8257d-fb14-57d5-9e12-a487533a6ca8.png)
1. pinpointに対して、エンドポイントの更新
![スクリーンショット 2022-06-18 18.12.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/19b3b5c0-c6de-df13-dd24-cf500a0b50a6.png)
2. pinpointに対して、セグメントを作成
![スクリーンショット 2022-06-22 22.57.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/1db27612-0eff-6121-ba51-264eb3fff661.png)
2. pinpointに対して、セグメントとキャンペーンを作成
![スクリーンショット 2022-06-16 21.21.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/11216928-7824-8294-ceb7-8f060f7d4379.png)
2. pinpointに対して、セグメントとキャンペーンを更新
![スクリーンショット 2022-06-18 16.52.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/f9ec6815-2124-a9ca-9150-8d11863dbfb1.png)
2. pinpointに対して、セグメントとキャンペーンを削除
![スクリーンショット 2022-06-18 16.53.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/aa56a6b4-adf8-2296-699f-4b5a1809c4c4.png)

## 前提条件
- push通知は、iosとandroidを対象


# 用語の説明
用語の説明は、`通知タイプの内push通知を意識した説明`にします。
## エンドポイント
エンドポイントとは、push通知の送信先であるデバイス情報のことです。
デバイストークンなどの情報をエンドポイントとしてpinpointに登録すると、pinpointからエンドポイント（デバイス）に対してpush通知を送ることができます。
pinpointに登録方法は、API または AWS SDK を使用して追加できます。今回は、Lambdaを使用して追加します。

### カスタム属性
エンドポイントには、デバイスの情報だけでなく、セグメントのフィルタリング時に使用するカスタム属性などを含めることができます。
カスタム属性とは、アプリ内でユーザーが登録している情報になります。例えば、ユーザーのお気に入りに商品や住所などの情報などです。



## セグメント
セグメントとは、push通知を送信する対象を、特定の属性や条件でフィルタリングしたグループのことです。

pinpointは、全てのエンドポイントがpinpoint内でDBとして保存されているイメージです。
pinpointに保存されている全てのエンドポイントを`基本セグメント`といいます。
基本セグメントから、動的セグメント（後述）によって、push通知先をフィルタリングすることができます。

特定の商品をお気に入りにしているユーザーのみにpush通知を送りたい場合、基本セグメントから、カスタム属性を使用してフィルタリングし、セグメントを作成します。


## セグメントを作成する方法は２つ
- 動的セグメント
    - エンドポイントのデバイス情報やカスタム属性に基づき、動的に送信先が変化するセグメントです。
        - エンドポイントの登録している住所やお気に入りに商品が変わると、定義しているセグメントから外れることもあるため、動的セグメントは時間の経過とともに変化する可能性があります。


- 静的セグメント
    -  エンドポイントのデバイス情報やカスタム属性を事前に記載したファイルに基づいて作成されるセグメントです。
    -  インポートセグメントを作成するときは、S3 にファイルをアップロードします。静的なので、時間の経過とともに変更はされません。
    -  `今回こちらは使用しません。`





## キャンペーン
キャンペーンでは、定義したスケジュールに従ってカスタマイズされたメッセージを送信することができます。
キャンペーンで設定できる機能は、以下となっております。

- 送信方法を決める
    - push通知、SMS、アプリ内メッセージ、Email
- push通知時に設定できるメッセージ内容
    - タイトルや本文
- 通知のスケジュール
    - 頻度、日時、
- セグメントを選択し、通知先を指定する
- 1秒間あたりに送る件数変えられる

また、キャンペーンでpush通知すると、push通知によってアプリを開いたユーザーの割合等を分析することが可能です。



# プロジェクト作成
プロジェクト名を入力します
![スクリーンショット 2022-06-16 21.05.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/9d6d6011-543d-e873-85af-98033cf15d6e.png)
push通知を選択します
![スクリーンショット 2022-06-16 21.06.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/16d88ea3-f4f4-53bc-9c11-bef38728db52.png)
`APNs` と　`FC`M のセットアップ方法は、[こちらの記事を参考にしてください。](https://qiita.com/hirai-11/items/5677b75ebf2c9fe077a4)
![スクリーンショット 2022-06-16 21.07.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/97322dc0-8837-ca0c-9bbc-5703208b370c.png)
また、Lambdaで必要になる`プロジェクトID`をコピーしておいてください。
![スクリーンショット 2022-06-16 21.22.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/a3510b51-b7ff-5b96-83ec-9acf69526606.png)

# Lambdaを作成

Lambda側で行うことは以下の通りです。
- Pinpointに対してエンドポイントを登録する
- Pinpointに対してセグメントを登録する
- Pinpointに対してキャンペーンを登録する
になります。

さらに、登録だけでなく、更新と削除も可能です。
- Pinpointに対してエンドポイントを更新する
- Pinpointに対してセグメントを更新する
- Pinpointに対してキャンペーンを更新する

- Pinpointに対してエンドポイントを削除する
- Pinpointに対してセグメントを削除する
- Pinpointに対してキャンペーンを削除する

## Lambdaの基本設定
Node.jsで開発します。
![スクリーンショット 2022-06-16 22.01.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/5efb7369-e247-a298-138b-efbcbec0ae2a.png)

- タイムアウト30秒に変更
- 環境変数にpinpointのIDを追加します。
![スクリーンショット 2022-06-16 22.03.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/79faf4b9-b624-a2a9-543d-a7f9d01369ef.png)

## IAMロール
デフォルトのLambdaのIAMロールに以下のIAM権限を付与させます。
`<アカウントID>`は、アカウントIDになります
```json
{
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "mobiletargeting:UpdateSegment",
                "mobiletargeting:UpdateCampaign",
                "mobiletargeting:GetSegment",
                "mobiletargeting:GetCampaign",
                "mobiletargeting:DeleteCampaign",
                "mobiletargeting:DeleteSegment"
            ],
            "Resource": [
                "arn:aws:mobiletargeting:*:<アカウントID>:apps/*/segments/*",
                "arn:aws:mobiletargeting:*:<アカウントID>:apps/*/campaigns/*"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "mobiletargeting:CreateCampaign",
                "mobiletargeting:CreateSegment",
                "mobiletargeting:UpdateSegment",
                "mobiletargeting:GetSegment",
                "mobiletargeting:DeleteUserEndpoints",
                "mobiletargeting:GetEndpoint",
                "mobiletargeting:DeleteSegment",
                "mobiletargeting:GetUserEndpoints",
                "mobiletargeting:DeleteEndpoint",
                "mobiletargeting:GetCampaigns",
                "mobiletargeting:UpdateCampaign",
                "mobiletargeting:GetCampaign",
                "mobiletargeting:DeleteCampaign",
                "mobiletargeting:GetSegments",
                "mobiletargeting:UpdateEndpoint"
            ],
            "Resource": "arn:aws:mobiletargeting:*:<アカウントID>:apps/*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "mobiletargeting:CreateCampaign",
                "mobiletargeting:CreateSegment",
                "mobiletargeting:UpdateSegment",
                "mobiletargeting:GetSegment",
                "mobiletargeting:DeleteUserEndpoints",
                "mobiletargeting:GetEndpoint",
                "mobiletargeting:DeleteSegment",
                "mobiletargeting:GetUserEndpoints",
                "mobiletargeting:DeleteEndpoint",
                "mobiletargeting:GetCampaigns",
                "mobiletargeting:UpdateCampaign",
                "mobiletargeting:GetCampaign",
                "mobiletargeting:DeleteCampaign",
                "mobiletargeting:GetSegments",
                "mobiletargeting:UpdateEndpoint"
            ],
            "Resource": "arn:aws:mobiletargeting:*:<アカウントID>:apps/*"
        }
    ]
}
```



# 1. Pinpointに対してエンドポイントを登録する

![スクリーンショット 2022-06-18 18.11.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/71e8257d-fb14-57d5-9e12-a487533a6ca8.png)




flutter側で、PinpointのAPIを叩くことで、エンドポイントにデバイストークンなどの情報を保存できます。
詳しくは下記の記事が参考になります。
今回、flutterの詳しい実装方法は説明いたしません。

https://docs.aws.amazon.com/ja_jp/pinpoint/latest/developerguide/audience-define-endpoints.html

https://note.com/yusukemikami/n/n6d9b4ebdd860





# 2. Pinpointに対してエンドポイントを更新する
![スクリーンショット 2022-06-18 18.12.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/19b3b5c0-c6de-df13-dd24-cf500a0b50a6.png)

pinpointにエンドポイントを更新するために、flutterからLambdaに対して情報を送信します。
flutterからLambdaを呼ぶ方法として、apigatewayもしくは、Lambda単体でURLによって呼べるようになったため、以下の記事を参考に呼ぶとよいでしょう。

https://tech.nri-net.com/entry/lambda_url

IAM認証を無効にする場合、全世界からアクセスされる可能性があるため、
例えば、lambda内でBasic認証を設定し、flutterからURLでlambdaを呼ぶ際に、ヘッダーに`authorization`必要な値を加えてURLを呼ぶことで、セキュアにする必要がありますね。

## エンドポイントを取得するLambdaを作成

エンドポイントを取得するためには、ApplicationId と EndpointIdが必要です。
`EndpointId`の取得方法は、flutter側で、getUserEndpointsAPI を利用して、userIdからエンドポイントIdを取得できますが、今回は説明しません。

https://note.com/yusukemikami/n/n6d9b4ebdd860

https://docs.aws.amazon.com/ja_jp/pinpoint/latest/developerguide/audience-define-endpoints.html

詳細は、sdkのドキュメントを参照してください

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Pinpoint.html#getUserEndpoints-property



```js
const application_id = process.env.AWS_PINPOINT_PROJECT_ID;
const AWS = require('aws-sdk');
const pinpoint = new AWS.Pinpoint({ region: process.env.AWS_REGION });

const endpoint_id = 'eqmj8wpxszeqy/b3vch04sn41yw' //サンプルデータを使用

exports.handler = async (event) => {
  console.log('event:', JSON.stringify(event, null, 2));


  const params = {
    ApplicationId: application_id,
    EndpointId: endpoint_id
  };

  try {
    const response_endpoint = await pinpoint.getEndpoint(params).promise();
    console.log('response_endpoint:', JSON.stringify(response_endpoint, null, 2));
  } catch (err) {
    console.error(err);
  }
};
```
実行するとエンドポイントが取得できます。


今回、上記のEndpointIDは、サンプルCSVのセグメントデータから取得したものを使用しました。

### サンプルCSVのセグメントデータを取得する方法
pinpointのコンソールから`セグメント作成`に遷移し、サンプルCSVをダウンロードし、そのCSVファイルをドロップし、セグメント作成をクリックします。

![スクリーンショット 2022-06-18 18.48.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/692d2448-40ad-7b74-733d-02adb053e71a.png)

`pinpoint_example_import`をダウンロードします。

![スクリーンショット 2022-06-18 18.48.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/62dfb6da-3736-fd98-9b16-45480529ba17.png)

ファイルの左端の`id`が`EndpointID`になりますので、コピーします。

![スクリーンショット 2022-06-18 18.50.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/55b15774-a52f-addc-037c-a764aba403c6.png)

## エンドポイントを更新するLambdaを作成

エンドポイントを更新するためには、ApplicationId と EndpointIdが必要です。
`EndpointId`の取得方法は、flutter側で、getUserEndpointsAPI を利用して、userIdからエンドポイントIdを取得できますが、今回は説明しません。


```js
const application_id = process.env.AWS_PINPOINT_PROJECT_ID;
const AWS = require('aws-sdk');
const pinpoint = new AWS.Pinpoint({ region: process.env.AWS_REGION });
const endpoint_id = 'eqmj8wpxszeqy/b3vch04sn41yw'; //サンプルデータを使用

exports.handler = async (event) => {
  console.log('event:', JSON.stringify(event, null, 2));

  const endpoint_data = {
    ChannelType: 'GCM',
    Address: 'xxxx',
    EndpointStatus: 'ACTIVE',
    OptOut: 'NONE',
    Location: {
      Country: 'JPN',
    },
    Demographic: {
      Make: 'Google',
      Platform: 'android',
    },
    EffectiveDate: '2022-06-02T00:00:00+09:00',
    User: {
      UserAttributes: {
        Item: ['bag', 'shoes'],
        Gender: ['woman'],
      },
    },
  };
  const params = {
    ApplicationId: application_id,
    EndpointId: endpoint_id,
    EndpointRequest: endpoint_data,
  };

  try {
    const response_endpoint = await pinpoint.updateEndpoint(params).promise();
    console.log('response_endpoint:', JSON.stringify(response_endpoint, null, 2));
  } catch (err) {
    console.error(err);
  }
};
```

上記のコードの内、以下は、ユーザーがお気に入りに登録しているアイテム（bag,shoes）、ユーザーの性別情報になります。（こちらもあくまで想定です。）

```js
    User: {
      UserAttributes: {
        Item: ['bags', 'shoes'],
        Gender: ['woman'],
      },
    },
```

実行すると、レスポンスで`Accepted`になっており、エンドポイントの値を変更に成功しました。
```js
response_endpoint: {
  "MessageBody": {
    "Message": "Accepted",
    "RequestID": "07585a26-c5e2-42d6-8aab-7a4973c15baa"
  }
}
```


`updateEndpoint`の詳細は、sdkのドキュメントを参照してください

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Pinpoint.html#updateEndpoint-property


# 3. Pinpointに対してセグメントを登録する
![スクリーンショット 2022-06-18 15.53.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/4ebac12e-81c6-3c99-d957-be64f7f076b2.png)


Lambdaからapiに対してセール対象の商品情報を受け取り、セール対象のアイテムをお気に入り登録しているユーザーのみにpush通知されるよう、セグメントを作成します。（あくまで想定です。）

セグメントは、バッグまたはパンツをお気に入りに登録している女性に対して、セグメントを作成します。

つまり、ユーザー属性が`Item`のうち`bags,もしくはpants`を登録し、
ユーザー属性が`Gender`のうち`woman`を登録している女性のセグメントをLambdaで作成するということです。

## Lambdaが受け取るevent内容
```json
{
  "item": ["bags","pants"],
  "gender": "woman"
}
```
![スクリーンショット 2022-06-18 15.51.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/8aa2c4c6-9e24-9fb7-b755-e492928f1296.png)

## セグメントのみを登録するLambdaを作成

詳細は、sdkのドキュメントを参照してください

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Pinpoint.html#createSegment-property

```js
const application_id = process.env.AWS_PINPOINT_PROJECT_ID;

const AWS = require('aws-sdk');
const pinpoint = new AWS.Pinpoint({ region: process.env.AWS_REGION });

exports.handler = async (event) => {
  console.log('event:', JSON.stringify(event, null, 2));

  const segment_data = {
    Name: 'favorite_segment',
    Dimensions: {
      UserAttributes: {
        Item: {
          AttributeType: 'INCLUSIVE',
          Values: event['item'],
        },
        Gender: {
          AttributeType: 'INCLUSIVE',
          Values: event['gender'].split(','),
        },
      },
    },
  };

  try {
    const params_segment = {
      ApplicationId: application_id,
      WriteSegmentRequest: segment_data,
    };
    const segment_response = await pinpoint.createSegment(params_segment).promise();
    console.log('segment_response:', JSON.stringify(segment_response, null, 2));

    return pinpoint_response['SegmentResponse']['Id'];
  } catch (err) {
    console.error(err);
  }
};
```
returnでは 作成した`セグメントID`を返しています。
作成できていることを確認しました。
![スクリーンショット 2022-06-18 15.46.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/15b23d2b-285c-3825-dd17-1334f6670050.png)


### Values内は、配列
注意点として、`Values`内は、配列になっていることに気をつけましょう。
- `Values: event['item']`
- `Values: event['gender'].split(',')`
↑にするため`split`メソッドを使用して、文字列を配列化するよう処理しています。

### AttributeType
`AttributeType: 'INCLUSIVE',`は、`Values`と一致する属性値をもつエンドポイントがセグメントに入ります。
`EXCLUSIVE`の場合、`Values`と一致しない属性値をもつエンドポイントがセグメントに入ります。

# 4. Pinpointに対して、セグメントを作成し、キャンペーンを登録する
先程セグメントのみを作成するLambdaを作成しましたが、実際には、セグメントを作成し、キャンペーン作成までを1つのLambdaで作成させるのがよいでしょう。
![スクリーンショット 2022-06-18 15.40.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/b8113a6a-c365-d82d-f59f-735e5a641e19.png)

## Lambdaが受け取るevent内容
先程と同じです。
```json
{
  "item": ["bags","pants"],
  "gender": "woman"
}
```
## セグメントを作成し、キャンペーンを登録するLambdaを作成

```js
const application_id = process.env.AWS_PINPOINT_PROJECT_ID;

const AWS = require('aws-sdk');
const pinpoint = new AWS.Pinpoint({ region: process.env.AWS_REGION });

function write_campaign_data(
  messages_per_second,
  ttl,
  push_title,
  campaign_title,
  push_body,
  schedule,
  segment_id
) {
  const APNS_row_content = {
    aps: {
      alert: {
        title: push_title,
        body: push_body,
      },
      sound: 'default',
      content_available: true,
    },
  };
  const GCM_row_content = {
    data: {
      title: push_title,
      body: push_body,
      jsonBody: {
        type: campaign_title,
      },
    },
  };
  return {
    AdditionalTreatments: [],
    Description: '',
    HoldoutPercent: 0,
    IsPaused: false,
    Limits: {
      Daily: 0,
      MaximumDuration: 10800,
      MessagesPerSecond: messages_per_second,
      Total: 0,
    },
    MessageConfiguration: {
      APNSMessage: {
        Action: 'OPEN_APP',
        TimeToLive: ttl,
        RawContent: JSON.stringify(APNS_row_content),
      },
      GCMMessage: {
        Action: 'OPEN_APP',
        TimeToLive: ttl,
        RawContent: JSON.stringify(GCM_row_content),
      },
    },
    Name: campaign_title,
    Schedule: {
      Frequency: 'ONCE',
      StartTime: schedule,
      Timezone: 'UTC+09',
    },
    SegmentId: segment_id,
    SegmentVersion: 1,
  };
}

exports.handler = async (event) => {
  console.log('event:', JSON.stringify(event, null, 2));

  const segment_data = {
    Name: 'favorite_segment',
    Dimensions: {
      UserAttributes: {
        Item: {
          AttributeType: 'INCLUSIVE',
          Values: event['item'],
        },
        Gender: {
          AttributeType: 'INCLUSIVE',
          Values: event['gender'].split(','),
        },
      },
    },
  };
  let response_segment_id;

  try {
    const params_segment = {
      ApplicationId: application_id,
      WriteSegmentRequest: segment_data,
    };
    const segment_response = await pinpoint.createSegment(params_segment).promise();
    console.log('segment_response:', JSON.stringify(segment_response, null, 2));

    response_segment_id = segment_response['SegmentResponse']['Id'];
  } catch (err) {
    console.error(err);
  }

  const campaign_data = write_campaign_data(
    100, // int:MessagesPerSecond
    3600, // int:TimeToLive
    'test_title', // str:push_title
    'test_campaign', // str:campaign_title
    'test_body', // str:push_body
    'IMMEDIATE', // str:schedule
    response_segment_id // str:segment_id
  );
  console.log('campaign_data:', JSON.stringify(campaign_data, null, 2));

  try {
    const params_campaign = {
      ApplicationId: application_id,
      WriteCampaignRequest: campaign_data,
    };
    await pinpoint.createCampaign(params_campaign).promise();
  } catch (err) {
    console.error(err);
  }
};
```
キャンペーンが作成されました。
![スクリーンショット 2022-06-18 16.12.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/f7a6af3b-b1f5-8585-a165-51bf60bf5e86.png)

## キャンペーンの設定内容詳細
上記のコードの内、キャンペーンの設定内容詳細である`function write_campaign_data`についてを説明します。

### 通知タイプ
プッシュ通知には、通知タイプが３つあります。
- 標準メッセージ
    - メッセージのタイトルと本文のほか、受信者が通知を開いたときに発生するアクションを指定するタイプ
- サイレント通知
    - 受信者のデバイスに通知を表示せずにユーザーのアプリケーションに送信されるタイプ
- raw メッセージ
    - JSON オブジェクトにプッシュ通知の raw コンテンツを指定するタイプ

私の場合、標準メッセージタイプだと、IOSにpush通知した際、デバイス側で通知音がしなかったため、rawメッセージタイプを選択しました。

rawメッセージタイプだと、通知音が指定できるため、IOSにpush通知した際、push通知音が出ました。

### android側のjsonBody
android側には、jsonBodyに`type: campaign_title`を入れています。
これは、fluuter側の事情で、私の場合、キャンペーンタイトル情報を保持する必要があったためです。jsonBody自体なくても問題ありません。
```js
function write_campaign_data(
  messages_per_second,
  ttl,
  push_title,
  campaign_title,
  push_body,
  schedule,
  segment_id
) {
  const APNS_row_content = {
    aps: {
      alert: {
        title: push_title,
        body: push_body,
      },
      sound: 'default',       // push通知音を指定
      content_available: true,
    },
  };
  const GCM_row_content = {
    data: {
      title: push_title,
      body: push_body,
      jsonBody: {
        type: campaign_title, //android側にtitle情報を保持する必要があったため
      },
    },
  };
```

### その他キャンペーン内容で設定できる数値

push通知のキャンペーン内容で設定できる値を説明します。
- Limits['MaximumDuration']
    - スケジュールされた開始時刻以降に各キャンペーンがメッセージの配信を試行できる最大秒数。
    - メッセージの配信が失敗した際、再試行する制限時間を設定できます
    
- Limits['MessagesPerSecond']
    - １秒間にエンドポイント（デバイス）に送信する件数
    - 1~25000件まで選択可能
    
- MessageConfiguration['TimeToLive']
    - Pinpoint がメッセージを削除するまでにかかる時間 (秒)
    - この時間が経過すると、Amazon Pinpoint はメッセージを削除し、再配信の試行を行いません。
    
- MessageConfiguration['RawContent']
    - push通知の通知タイプ
    
- Name
    - キャンペーン名
    
- Schedule['StartTime]
    - キャンペーンを送る日時
        - 'IMMEDIATE':即時
        - "2022-01-01T19:00:00+09:00": 2022年1月1日19時にキャンペーンを送信
        
- Schedule['Frequency]
    - キャンペーンの頻度
        - 'ONCE':1回のみ
        - 'DAILY':毎日
        
- SegmentId
    - Lambdaで作成したセグメントのID



```js
  return {
    AdditionalTreatments: [],
    Description: '',
    HoldoutPercent: 0,
    IsPaused: false,
    Limits: {
      Daily: 0,
      MaximumDuration: 10800,
      MessagesPerSecond: messages_per_second,
      Total: 0,
    },
    MessageConfiguration: {
      APNSMessage: {
        Action: 'OPEN_APP',
        TimeToLive: ttl,
        RawContent: JSON.stringify(APNS_row_content),
      },
      GCMMessage: {
        Action: 'OPEN_APP',
        TimeToLive: ttl,
        RawContent: JSON.stringify(GCM_row_content),
      },
    },
    Name: campaign_title,
    Schedule: {
      Frequency: 'ONCE',
      StartTime: schedule,
      Timezone: 'UTC+09',
    },
    SegmentId: segment_id,
    SegmentVersion: 1,
  };
}
```

詳細は、sdkのドキュメントを参照してください

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Pinpoint.html#createCampaign-property

長くなったため、次回、「セグメント、キャンペーン」の「更新、削除」をそれぞれ行います。
↓続きは、こちらの記事です。

https://qiita.com/hirai-11/items/057aacd5c06db5c7f78c

# 参考

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Pinpoint.html

https://d1.awsstatic.com/webinars/jp/pdf/services/20171109_AWS-BlackBelt-pinpoint.pdf

https://qiita.com/honma12345/items/c61a0f5c87ad7923bbd4

https://note.com/yusukemikami/n/n6d9b4ebdd860

https://docs.kii.com/ja/guides/cloudsdk/rest/managing-push-notification/push-overview/structure/

https://qiita.com/fujisan3/items/d037e3c40a0acc46f618

https://qiita.com/yosshi4486/items/7dbd7e2bc35a1fc9e5a0

https://future-architect.github.io/articles/20220323a/


https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/using-async-await.html

- async await promise()

https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification

- IOSのプッシュ通知音の設定について