ASP.NET Firebaseを使ってプッシュ通知を送信する (1)

本日は、ASP.NETからFirebase経由でプッシュ通知を送ります。　主に社内共有用なので色々な説明をスキップします。

# Firebase設定方法

Firebaseの設定方法などの細かい話は一旦スキップします。プロジェクト→設定→サービスアカウント→新しい鍵を生成をクリックするとjsonファイルが生成されるので、そのファイルをASP.NETプロジェクトのwwwwrootフォルダーに入れておきます、またはAzure Key Vaultなどを使いアクセス制限をかけた状態でアクセスします。

# ライブラリー

下記のライブラリーを使います


```
Install-Package FirebaseAdmin
```

```
Install-Package PushSharp
```

# コード

下記が完成したコードです。前提としてその対象ユーザーの端末Tokenを持っていることとします。

```csharp

public async Task SendPushNotificationAsync(string deviceToken, string title, string body)
{
    　string serviceAccountJsonPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "YOURKEYFIRLE.json");
     string projectId = "YOURPROJECTID";
     
     GoogleCredential googleCredential;
     using (var stream = new FileStream(serviceAccountJsonPath, FileMode.Open, FileAccess.Read))
    {
         googleCredential = GoogleCredential.FromStream(stream)
         .CreateScoped("https://www.googleapis.com/auth/firebase.messaging");
    }

    var accessToken = await googleCredential.UnderlyingCredential.GetAccessTokenForRequestAsync();

    var messageInformation = new
    {
        message = new
        {
            token = deviceToken,
            notification = new
            {
                title = title,
                body = body
            }
        }
    };

    var jsonMessage = JsonSerializer.Serialize(messageInformation);

    var request = new HttpRequestMessage(HttpMethod.Post, $"https://fcm.googleapis.com/v1/projects/{projectId}/messages:send");
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    request.Content = new StringContent(jsonMessage, Encoding.UTF8, "application/json");

    HttpResponseMessage result;
    try
    {
        result = await _httpClient.SendAsync(request);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error sending push notification: " + ex.Message);
        throw;
    }

    if (result.IsSuccessStatusCode)
    {
        Console.WriteLine("Notification sent successfully.");
    }
    else
    {
        Console.WriteLine("Failed to send notification. Status code: " + result.StatusCode);
        var responseBody = await result.Content.ReadAsStringAsync();
        Console.WriteLine("Response body: " + responseBody);
    }
}
```

#　メッセージタイプ

プッシュ通知を送る際にはいくつかパターンが考えられます。

## 通常メッセージ

シンプルにメッセージを添えてプッシュしたい場合はこちらです。

```csharp

public async Task SendNotificationMessage(string token, string title, string body)
{
    var message = new Message()
    {
        Token = token,
        Notification = new Notification
        {
            Title = title,
            Body = body
        }
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent notification message: " + response);
}

```

## データ

何かしらデータをつけて送る場合はこちらです。ここに付与したデータを元に表示する内容などをアプリ側で制御します。

```csharp 

public async Task SendDataMessage(string token, Dictionary<string, string> data)
{
    var message = new Message()
    {
        Token = token,
        Data = data
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent data message: " + response);
}
```

## データとメッセージ

データとメッセージ両方を使うパターンです。

```csharp 

public async Task SendNotificationWithDataMessage(string token, string title, string body, Dictionary<string, string> data)
{
    var message = new Message()
    {
        Token = token,
        Notification = new Notification
        {
            Title = title,
            Body = body
        },
        Data = data
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent notification with data message: " + response);
}
```

## マルチキャスト

複数の端末に（同じ内容を）送信したい場合にはこれを使います。

```csharp 

public async Task SendMulticastMessage(List<string> tokens, string title, string body)
{
    var message = new MulticastMessage()
    {
        Tokens = tokens,
        Notification = new Notification
        {
            Title = title,
            Body = body
        }
    };

    var response = await FirebaseMessaging.DefaultInstance.SendMulticastAsync(message);
    Console.WriteLine($"Successfully sent multicast message: {response.SuccessCount} out of {tokens.Count}");
}
```

## トピック別

購読しているトピックユーザーに対して送信します。

```csharp 

public async Task SendTopicMessage(string topic, string title, string body)
{
    var message = new Message()
    {
        Topic = topic,
        Notification = new Notification
        {
            Title = title,
            Body = body
        }
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent topic message: " + response);
}
```

## 条件ありトピック

AもBも購読しているユーザーのみ、などのコンディションを追加したい場合はこちらです。

```csharp 

public async Task SendConditionalMessage(string condition, string title, string body)
{
    var message = new Message()
    {
        Condition = condition,
        Notification = new Notification
        {
            Title = title,
            Body = body
        }
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent conditional message: " + response);
}
```

こんな感じに条件をつけることができる。

```csharp 

// Example: Send a message to devices subscribed to "TopicA" and "TopicB"
string condition = "'TopicA' in topics && 'TopicB' in topics";
await SendConditionalMessage(condition, "Special Offer!", "Get 50% off on your next purchase.");

// Example: Send a message to devices subscribed to "TopicA" or "TopicB", but not "TopicC"
string condition = "('TopicA' in topics || 'TopicB' in topics) && !('TopicC' in topics)";
await SendConditionalMessage(condition, "Exclusive Update!", "This message is for special users.");

```

## イメージ

プッシュ通知にイメージをつけたい場合はこちら

```csharp 

public async Task SendImageNotification(string token, string title, string body, string imageUrl)
{
    var message = new Message()
    {
        Token = token,
        Notification = new Notification
        {
            Title = title,
            Body = body,
            ImageUrl = imageUrl
        }
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent image notification: " + response);
}
```

## プライオリティ高メッセージ

プラオリティ高のメッセージを送る場合はこちら

```csharp 

public async Task SendPriorityMessage(string token, string title, string body)
{
    var message = new Message()
    {
        Token = token,
        Notification = new Notification
        {
            Title = title,
            Body = body
        },
        Android = new AndroidConfig
        {
            Priority = Priority.High
        }
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent high priority message: " + response);
}
```

## サイレントメッセージ

ポップアップされないけど、通知する場合はこちら。

```csharp 

public async Task SendSilentNotification(string token, Dictionary<string, string> data)
{
    var message = new Message()
    {
        Token = token,
        Data = data,
        Android = new AndroidConfig
        {
            Priority = Priority.High
        },
        Apns = new ApnsConfig
        {
            Aps = new Aps
            {
                ContentAvailable = true
            }
        }
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent silent notification: " + response);
}
```

# 付録：　データを送った場合のSwift側ので処理


プッシュ通知にデータを付与

```csharp
public async Task SendDataMessage(string token, string action, string view, string chatId)
{
    var message = new Message()
    {
        Token = token,
        Data = new Dictionary<string, string>()
        {
            { "action", action },
            { "view", view },
            { "chatId", chatId }
        }
    };

    string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
    Console.WriteLine("Successfully sent data message: " + response);
}
```

プッシュ通知をタップした後の処理

```swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
    if let action = userInfo["action"] as? String {
        if action == "open_view" {
            if let view = userInfo["view"] as? String, view == "chat" {
                if let chatId = userInfo["chatId"] as? String {
                    // Navigate to chat view
                    let chatViewController = ChatViewController()
                    chatViewController.chatId = chatId
                    if let rootViewController = window?.rootViewController {
                        rootViewController.present(chatViewController, animated: true, completion: nil)
                    }
                }
            }
            // Handle other views here
        }
    }
}
```

# 紹介



## レシート読み込みサービス（LINE)

- LINEでレシートの写真を撮るだけで、電子レシートになるサービス
https://lin.ee/mRW4X2c