5using 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>()
228 Dictionary<string, object> Request =
new Dictionary<string, object>()
234 Request[
"validate_only"] =
true;
239 if (!(Data is
null) && Data.Count > 0)
241 Dictionary<string, object> ConvertedData =
new Dictionary<string, object>();
242 foreach (KeyValuePair<string, object> Kvp
in Data)
246 if (Kvp.Value is Dictionary<string, object> NestedObj)
248 ConvertedData[Kvp.Key] =
JSON.
Encode(NestedObj,
false);
250 else if (Kvp.Value is IEnumerable Enumerable && !(Kvp.Value is
string))
252 List<object> Items =
new List<object>();
253 foreach (
object Item
in Enumerable)
256 ConvertedData[Kvp.Key] =
JSON.
Encode(Items.ToArray(),
false);
260 ConvertedData[Kvp.Key] = Kvp.Value?.ToString() ??
string.Empty;
263 Msg[
"data"] = ConvertedData;
270 StringBuilder sb =
new StringBuilder();
273 sb.Append(this.sendNotificationUri.ToString());
275 sb.Append(
" Authorization: Bearer ");
280 s = s.Replace(
"\t",
" ");
282 string[] Rows = s.Split(
CommonTypes.
CRLF, StringSplitOptions.RemoveEmptyEntries);
284 foreach (
string Row
in Rows)
296 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + this.
BearerToken));
302 if (ex.Content is Dictionary<string, object> ErrorResponse &&
303 ErrorResponse.TryGetValue(
"error", out
object Obj) &&
304 Obj is Dictionary<string, object> ErrorObject &&
305 ErrorObject.TryGetValue(
"message", out Obj) &&
306 Obj is
string ErrorMessage)
308 this.
Error(ErrorMessage);
332 if (!(Response.
Decoded is Dictionary<string, object> Obj))
Helps with parsing of commong data types.
static readonly char[] CRLF
Contains the CR LF character sequence.
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Exception Error
Error response.
Exception class for web exceptions.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > 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.
void TransmitText(string Text)
Called when text has been transmitted.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
void ReceiveText(string Text)
Called when text has been received.
ISniffer[] Sniffers
Registered sniffers.
bool HasSniffers
If there are sniffers registered on the object.
void Error(string Error)
Called to inform the viewer of an error state.
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 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...