2using System.Collections.Generic;
4using System.Threading.Tasks;
28 private static readonly Uri productionEndpoint =
new Uri(
"https://fcm.googleapis.com/");
29 private const string scope =
"https://www.googleapis.com/auth/firebase.messaging";
31 private readonly
string projectId;
32 private readonly
string privateKeyId;
33 private readonly
string clientEmail;
34 private readonly
string clientId;
35 private readonly
string authUri;
36 private readonly
string tokenUri;
37 private readonly
string universeDomain;
38 private readonly
bool test;
39 private readonly Uri sendNotificationUri;
41 private string token =
null;
42 private DateTime tokenTimestamp = DateTime.MinValue;
55 this.factory = Factory;
57 this.sendNotificationUri =
new Uri(productionEndpoint,
"v1/projects/" + this.projectId +
"/messages:send");
98 public bool Test => this.test;
109 if (!(
JSON.
Parse(GoogleServiceAccountJson) is Dictionary<string, object> Parsed) ||
110 !Parsed.TryGetValue(
"type", out
object Obj) || !(Obj is
string Type))
112 throw new ArgumentException(
"Invalid Google Service Account JSON file.", nameof(GoogleServiceAccountJson));
115 if (Type !=
"service_account")
116 throw new ArgumentException(
"File does not specify a Google Service account.", nameof(GoogleServiceAccountJson));
118 if (!Parsed.TryGetValue(
"project_id", out Obj) || !(Obj is
string ProjectId) ||
119 !Parsed.TryGetValue(
"private_key_id", out Obj) || !(Obj is
string PrivateKeyId) ||
120 !Parsed.TryGetValue(
"private_key", out Obj) || !(Obj is
string PrivateKey) ||
121 !Parsed.TryGetValue(
"client_email", out Obj) || !(Obj is
string ClientEmail) ||
122 !Parsed.TryGetValue(
"client_id", out Obj) || !(Obj is
string ClientId) ||
123 !Parsed.TryGetValue(
"auth_uri", out Obj) || !(Obj is
string AuthUri) ||
124 !Parsed.TryGetValue(
"token_uri", out Obj) || !(Obj is
string TokenUri) ||
125 !Parsed.TryGetValue(
"universe_domain", out Obj) || !(Obj is
string UniverseDomain))
127 throw new ArgumentException(
"Missing properties in JSON file.", nameof(GoogleServiceAccountJson));
138 private static readonly
Expression expCreateFactory =
new Expression(
"CreateJwtFactory(RS256(RsaFromPem(PrivateKey)))");
151 #region Authentication
160 DateTime Now = DateTime.Now;
162 if (this.token is
null || Now.Subtract(
this.tokenTimestamp).TotalMinutes >= 30)
164 int IssuedAt = (int)Math.Round(DateTime.UtcNow.Subtract(
JSON.
UnixEpoch).TotalSeconds);
165 int Expires = IssuedAt + 3600;
167 this.token = this.factory.
Create(
new KeyValuePair<string, object>[]
169 new KeyValuePair<string, object>(
"kid", this.privateKeyId),
171 new KeyValuePair<string, object>[]
175 new KeyValuePair<string, object>(
JwtClaims.
Audience, productionEndpoint.OriginalString),
180 this.tokenTimestamp = Now;
189 #region Send Notification
224 Dictionary<string, object> Msg =
new Dictionary<string, object>()
229 Dictionary<string, object> Request =
new Dictionary<string, object>()
235 Request[
"validate_only"] =
true;
240 if (!(Data is
null) && Data.Count > 0)
242 Dictionary<string, object> Updated =
new Dictionary<string, object>();
244 foreach (KeyValuePair<string, object> P
in Data)
246 if (P.Value is Dictionary<string, object> NestedObj)
249 Updated =
new Dictionary<string, object>();
255 if (!(Updated is
null))
257 foreach (KeyValuePair<string, object> P
in Updated)
258 Data[P.Key] = P.Value;
268 StringBuilder sb =
new StringBuilder();
271 sb.Append(this.sendNotificationUri.ToString());
273 sb.Append(
" Authorization: Bearer ");
278 s = s.Replace(
"\t",
" ");
280 string[] Rows = s.Split(
CommonTypes.
CRLF, StringSplitOptions.RemoveEmptyEntries);
282 foreach (
string Row
in Rows)
294 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + this.
BearerToken));
299 if (!(Response is Dictionary<string, object> Obj))
300 throw new Exception(
"Invalid or unexpected JSON content returned.");
306 if (ex.
Content is Dictionary<string, object> ErrorResponse &&
307 ErrorResponse.TryGetValue(
"error", out
object Obj) &&
308 Obj is Dictionary<string, object> ErrorObject &&
309 ErrorObject.TryGetValue(
"message", out Obj) &&
310 Obj is
string ErrorMessage)
312 await this.
Error(ErrorMessage);
Helps with parsing of commong data types.
static readonly char[] CRLF
Contains the CR LF character sequence.
Exception class for web exceptions.
object Content
Decoded content.
Static class managing encoding and decoding of internet content.
static Task< object > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
Helps with common JSON-related tasks.
static object Parse(string Json)
Parses a JSON string.
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
static string Encode(string s)
Encodes a string for inclusion in JSON.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Simple base class for classes implementing communication protocols.
Task Error(string Error)
Called to inform the viewer of an error state.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
ISniffer[] Sniffers
Registered sniffers.
Task TransmitText(string Text)
Called when text has been transmitted.
bool HasSniffers
If there are sniffers registered on the object.
Task ReceiveText(string Text)
Called when text has been received.
HTTP-based Firebase client.
string ClientEMail
Client e-Mail address, as from Service Account JSON.
static async Task< FirebaseClient > CreateAsync(string GoogleServiceAccountJson, bool Test, params ISniffer[] Sniffers)
Creates an HTTP-based Firebase client.
string AuthUri
Authentication URI, as from Service Account JSON.
string PrivateKeyId
Private Key ID, as from Service Account JSON.
string ClientId
Client ID, as from Service Account JSON.
Task< NotificationResponse > SendNotification(string To, NotificationMessage Message)
Sends a push notification
Task< NotificationResponse > SendNotification(string To, NotificationMessage Message, NotificationOptions Options)
Sends a push notification
void Dispose()
IDisposable.Dispose
string UniverseDomain
Universe Domain, as from Service Account JSON.
string ProjectId
Project ID, as from Service Account JSON.
async Task< NotificationResponse > SendNotification(string To, NotificationMessage Message, NotificationOptions Options, Dictionary< string, object > Data)
Sends a push notification
bool Test
If notifications should only be validated, as from Service Account JSON.
string TokenUri
Token URI, as from Service Account JSON.
string BearerToken
Bearer JWT token to use for authentication.
Base class for Notification messages
virtual Dictionary< string, object > GetNotificationObject()
Gets the notification object for a push notification.
virtual void ExportProperties(Dictionary< string, object > Message)
Prepares the object to send to Firebase.
void SetProperties(Dictionary< string, object > Message, FirebaseClient Client)
Prepares the object to send to Firebase.
Firebase response to sending a notification message.
Class managing a script expression.
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Static class containing predefined JWT claim names.
const string Issuer
Issuer of the JWT
const string Audience
Recipient for which the JWT is intended
const string IssueTime
Time at which the JWT was issued; can be used to determine age of the JWT
const string Subject
Subject of the JWT (the user)
const string ExpirationTime
Time after which the JWT expires
A factory that can create and validate JWT tokens.
string Create(params KeyValuePair< string, object >[] Claims)
Creates a new JWT token.
void Dispose()
IDisposable.Dispose
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...