FCMのPythonSDKでの通知メッセージ・データメッセージ・APNsの利用

# 通知メッセージとデータメッセージとAPNs
FCMのメッセージ配信は、通知メッセージとデータメッセージが存在します。通知メッセージはFCMが用意したフォーマットで配信することで、PUSH通知を送ることができます。PUSHメッセージはPUSH通知で利用するデータを送信し端末のアプリ側で処理を行います。またiOSの場合はAPNsを利用した配信も可能です。

# PythonSDKでのメッセージ配信
PythonSDKのリファレンスは以下になります。

https://firebase.google.com/docs/reference/admin/python/firebase_admin.messaging?hl=ja#notification

## 通知メッセージでの配信
以下のようにnotificationを指定することで通知メッセージでの配信ができます。

```python
notification = messaging.Notification(
  title='プッシュ通知',
  body='プッシュ通知が来たよ！',
  image='hoge://yyyyyyyy'
)

message = messaging.Message(token = "dummy", notification = notification)
messaging.send(message)
```

## データメッセージでの配信
以下のように `Message` メソッドにdataをdict形式で渡すことでデータメッセージができます。

```python
data = {
  "title": 'プッシュ通知',
  "body":　'プッシュ通知が来たよ！',
  "url": 'hoge://yyyyyyyy',
}


message = messaging.Message(token = "dummy", data = data)
messaging.send(message)
```

## APNsの利用
次にAPNsを利用する方法を紹介します。APNsを利用する場合基本的には通知メッセージまたはデータメッセージと組み合わせることが一般的です。基本的には、APNsに対応するように引数が存在しますが、存在しないものは以下のように `custom_data` を指定します。これにより、どのようなAPNsのパラメータも利用することができます。

```python
data = {
  "url": 'hoge://yyyyyyyy',
}
apns = messaging.APNSConfig(
  payload = messaging.APNSPayload(
    aps = messaging.Aps(
      alert = messaging.ApsAlert(
        body = 'プッシュ通知が来たよ！'
      ),
      sound = 'default',
      badge = 1,
      custom_data = {
        'interruption-level': 'time-sensitive'
      },
    )
  )
)

message = messaging.Message(token = "dummy", data = data, apns = apns)
messaging.send(message)
```

APNsのドキュメントは以下になります

https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW1

## その他
以上で紹介したものの他にWebプッシュ用のパラメータや、Andoroidだけで利用するためのパラメータ等も存在します。詳しくは以下のリファレンスをご参照ください。

https://firebase.google.com/docs/cloud-messaging/concept-options

https://firebase.google.com/docs/reference/admin/python/firebase_admin.messaging?hl=ja#message


# デバッグ
続いてPythonSDKで生成されたメッセージがどのようなJSONになるのかのデバッグ方法を紹介します。以下のように文字列変換してやることでJSONに変換可能です。ただし、 `Message` は `token` または `topic` のどちらかが必須となるためデータだけをデバッグしたい場合はダミーで値を登録してやる必要があります。

以下がデバッグの例です。

```python
data = {
  "url": 'hoge://yyyyyyyy',
}
apns = messaging.APNSConfig(
  payload = messaging.APNSPayload(
    aps = messaging.Aps(
      alert = messaging.ApsAlert(
        body = 'PUSH!!!!'
      ),
      sound = 'default',
      badge = 1,
      custom_data = {
        'interruption-level': 'time-sensitive'
      },
    )
  )
)

debug_message = messaging.Message(token = "dummy", data = data, apns = apns)
print(str(debug_message))
```

上記の結果が以下になります。

```json
{
  "apns": {
    "payload": {
      "aps": {
        "alert": {
          "body": "PUSH!!!!"
        },
        "badge": 1,
        "interruption-level": "time-sensitive",
        "sound": "default"
      }
    }
  },
  "data": {
    "url": "hoge://yyyyyyyy"
  },
  "token": "dummy"
}
```

APNsで指定した `custom_data` の `interruption-level` がちゃんと `aps` 直下に含まれていることが確認できます。一点注意点ですが、日本語が入っている場合はutf8エンコードされてしまう場合があります。これについては、「json dump python 日本語」等でググってください。

## 補足
[MulticastMessage](https://firebase.google.com/docs/reference/admin/python/firebase_admin.messaging?hl=ja#multicastmessage) 同一のメッセージを渡したtokenのリストに配信するものもありますがこちらはJSONへの変換ができませんでした。

また、公式のドキュメントにはこのデバッグ方法は書かれていなく、ソースコードを確認してJSONに変換できることを確認できたため、アナウンスなくJSON変換できなくなる可能性があります。

https://github.com/firebase/firebase-admin-python/blob/master/firebase_admin/_messaging_encoder.py#L58

# まとめ
本記事では、FCMのPythonSDKでの配信処理について紹介しました。また、メッセージのデバッグ方法について紹介しました。