Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
PushNotificationService.cs
1using EDaler;
3using NeuroFeatures;
4using System.Text;
5using Waher.Content;
12
14{
18 [Singleton]
20 {
21 private readonly Dictionary<PushMessagingService, string> tokens = [];
22 private DateTime lastTokenCheck = DateTime.MinValue;
23
28 {
29 }
30
36 {
37 if (!string.IsNullOrEmpty(TokenInformation.Token))
38 {
39 lock (this.tokens)
40 {
42 }
43
44 await ServiceRef.XmppService.NewPushNotificationToken(TokenInformation);
45
46 try
47 {
49 }
50 catch (Exception ex)
51 {
52 ServiceRef.LogService.LogException(ex);
53 }
54 }
55 }
56
61
68 public bool TryGetToken(PushMessagingService Source, out string? Token)
69 {
70 lock (this.tokens)
71 {
72 return this.tokens.TryGetValue(Source, out Token);
73 }
74 }
75
76 private static async Task<bool> ForceTokenReport(TokenInformation TokenInformation)
77 {
78 string OldToken = await RuntimeSettings.GetAsync(Constants.Settings.PushNotificationToken, string.Empty);
79 DateTime ReportDate = await RuntimeSettings.GetAsync(Constants.Settings.PushNotificationReportDate, DateTime.MinValue);
80
81 return (DateTime.UtcNow.Subtract(ReportDate).TotalDays > 7) || (TokenInformation.Token != OldToken);
82 }
83
89 {
90 try
91 {
92 DateTime Now = DateTime.Now;
93
94 if (ServiceRef.XmppService.IsOnline &&
95 ServiceRef.XmppService.SupportsPushNotification &&
96 Now.Subtract(this.lastTokenCheck).TotalHours >= 1)
97 {
98 this.lastTokenCheck = Now;
99
100 if (TokenInformation is null)
101 {
102 TokenInformation = await ServiceRef.PlatformSpecific.GetPushNotificationToken();
103 if (string.IsNullOrEmpty(TokenInformation.Token))
104 return;
105 }
106
107 bool ForceReport = await ForceTokenReport(TokenInformation);
108
109 string Version = AppInfo.VersionString + "." + AppInfo.BuildString;
110 string PrevVersion = await RuntimeSettings.GetAsync(Constants.Settings.PushNotificationConfigurationVersion, string.Empty);
111 bool IsVersionChanged = Version != PrevVersion;
112
113 if (IsVersionChanged || ForceReport)
114 {
115 string? Token = TokenInformation.Token;
116
117 if (!string.IsNullOrEmpty(Token))
118 {
121 await ServiceRef.XmppService.ReportNewPushNotificationToken(Token, Service, ClientType);
122
125 }
126 }
127
128 if (IsVersionChanged)
129 {
130 // it will force the rules update if somehing goes wrong.
132 await ServiceRef.XmppService.ClearPushNotificationRules();
133
134 #region Message Rules
135
136 // Push Notification Rule, for chat messages received when offline:
137
138 StringBuilder Content = new();
139
140 Content.Append("FromJid:=GetAttribute(Stanza,'from');");
141 Content.Append("ToJid:=GetAttribute(Stanza,'to');");
142 Content.Append("FriendlyName:=RosterName(ToJid,FromJid);");
143 Content.Append("Content:=GetElement(Stanza,'content');");
144 Content.Append("{'myTitle':'");
145 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.MessageFrom)]));
146 Content.Append(" ' + FriendlyName,");
147 Content.Append("'myBody':InnerText(GetElement(Stanza,'body')),");
148 Content.Append("'fromJid':FromJid,");
149 Content.Append("'rosterName':FriendlyName,");
150 //Content.Append("'isObject':false,");
151 Content.Append("'isObject':exists(Content) and !empty(Markdown:= InnerText(Content)) and (Left(Markdown,2)='![' or (Left(Markdown,3)='```' and Right(Markdown,3)='```')),");
152 Content.Append("'channelId':'");
153 Content.Append(Constants.PushChannels.Messages);
154 Content.Append("',");
155 Content.Append("'content_available':true}");
156
157 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Chat, string.Empty, string.Empty,
158 Constants.PushChannels.Messages, "Stanza", string.Empty, Content.ToString());
159
160 #endregion
161
162 #region Petitions
163
164 // Push Notification Rule, for Identity Petition requests when offline.
165
166 Content.Clear();
167 Content.Append("E:=GetElement(Stanza,'petitionIdentityMsg');");
168 Content.Append("ToJid:=GetAttribute(Stanza,'to');");
169 Content.Append("FromJid:=GetAttribute(E,'from');");
170 Content.Append("FriendlyName:=RosterName(ToJid,FromJid);");
171 Content.Append("{'myTitle':'");
172 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.PetitionFrom)]));
173 Content.Append(" ' + FriendlyName,");
174 Content.Append("'myBody':GetAttribute(E,'purpose'),");
175 Content.Append("'fromJid':FromJid,");
176 Content.Append("'rosterName':FriendlyName,");
177 Content.Append("'channelId':'");
178 Content.Append(Constants.PushChannels.Petitions);
179 Content.Append("',");
180 Content.Append("'content_available':true}");
181
182 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "petitionIdentityMsg", ContractsClient.NamespaceLegalIdentitiesCurrent,
183 Constants.PushChannels.Petitions, "Stanza", string.Empty, Content.ToString());
184
185 // Push Notification Rule, for Contract Petition requests when offline.
186
187 Content.Clear();
188 Content.Append("E:=GetElement(Stanza,'petitionContractMsg');");
189 Content.Append("ToJid:=GetAttribute(Stanza,'to');");
190 Content.Append("FromJid:=GetAttribute(E,'from');");
191 Content.Append("FriendlyName:=RosterName(ToJid,FromJid);");
192 Content.Append("{'myTitle':'");
193 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.PetitionFrom)]));
194 Content.Append(" ' + FriendlyName,");
195 Content.Append("'myBody':GetAttribute(E,'purpose'),");
196 Content.Append("'fromJid':FromJid,");
197 Content.Append("'rosterName':FriendlyName,");
198 Content.Append("'channelId':'");
199 Content.Append(Constants.PushChannels.Petitions);
200 Content.Append("',");
201 Content.Append("'content_available':true}");
202
203 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "petitionContractMsg", ContractsClient.NamespaceSmartContractsCurrent,
204 Constants.PushChannels.Petitions, "Stanza", string.Empty, Content.ToString());
205
206 // Push Notification Rule, for Signature Petition requests when offline.
207
208 Content.Clear();
209 Content.Append("E:=GetElement(Stanza,'petitionSignatureMsg');");
210 Content.Append("ToJid:=GetAttribute(Stanza,'to');");
211 Content.Append("FromJid:=GetAttribute(E,'from');");
212 Content.Append("FriendlyName:=RosterName(ToJid,FromJid);");
213 Content.Append("{'myTitle':'");
214 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.PetitionFrom)]));
215 Content.Append(" ' + FriendlyName,");
216 Content.Append("'myBody':GetAttribute(E,'purpose'),");
217 Content.Append("'fromJid':FromJid,");
218 Content.Append("'rosterName':FriendlyName,");
219 Content.Append("'channelId':'");
220 Content.Append(Constants.PushChannels.Petitions);
221 Content.Append("',");
222 Content.Append("'content_available':true}");
223
224 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "petitionSignatureMsg", ContractsClient.NamespaceLegalIdentitiesCurrent,
225 Constants.PushChannels.Petitions, "Stanza", string.Empty, Content.ToString());
226
227 #endregion
228
229 #region Identities
230
231 // Push Notification Rule, for Identity Update events when offline.
232
233 Content.Clear();
234 Content.Append("E:=GetElement(Stanza,'identity');");
235 Content.Append("{'myTitle':'");
236 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.IdentityUpdated)]));
237 Content.Append("',");
238 Content.Append("'legalId':GetAttribute(E,'id'),");
239 Content.Append("'channelId':'");
240 Content.Append(Constants.PushChannels.Identities);
241 Content.Append("',");
242 Content.Append("'content_available':true}");
243
244 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "identity", ContractsClient.NamespaceLegalIdentitiesCurrent,
245 Constants.PushChannels.Identities, "Stanza", string.Empty, Content.ToString());
246
247 #endregion
248
249 #region Contracts
250
251 // Push Notification Rule, for Contract Creation events when offline.
252
253 Content.Clear();
254 Content.Append("E:=GetElement(Stanza,'contractCreated');");
255 Content.Append("{'myTitle':'");
256 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.ContractCreated)]));
257 Content.Append("',");
258 Content.Append("'contractId':GetAttribute(E,'contractId'),");
259 Content.Append("'channelId':'");
260 Content.Append(Constants.PushChannels.Contracts);
261 Content.Append("',");
262 Content.Append("'content_available':true}");
263
264 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "contractCreated", ContractsClient.NamespaceSmartContractsCurrent,
265 Constants.PushChannels.Contracts, "Stanza", string.Empty, Content.ToString());
266
267 // Push Notification Rule, for Contract Signature events when offline.
268
269 Content.Clear();
270 Content.Append("E:=GetElement(Stanza,'contractSigned');");
271 Content.Append("{'myTitle':'");
272 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.ContractSigned)]));
273 Content.Append("',");
274 Content.Append("'contractId':GetAttribute(E,'contractId'),");
275 Content.Append("'legalId':GetAttribute(E,'legalId'),");
276 Content.Append("'channelId':'");
277 Content.Append(Constants.PushChannels.Contracts);
278 Content.Append("',");
279 Content.Append("'content_available':true}");
280
281 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "contractSigned", ContractsClient.NamespaceSmartContractsCurrent,
282 Constants.PushChannels.Contracts, "Stanza", string.Empty, Content.ToString());
283
284 // Push Notification Rule, for Contract Update events when offline.
285
286 Content.Clear();
287 Content.Append("E:=GetElement(Stanza,'contractUpdated');");
288 Content.Append("{'myTitle':'");
289 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.ContractUpdated)]));
290 Content.Append("',");
291 Content.Append("'contractId':GetAttribute(E,'contractId'),");
292 Content.Append("'channelId':'");
293 Content.Append(Constants.PushChannels.Contracts);
294 Content.Append("',");
295 Content.Append("'content_available':true}");
296
297 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "contractUpdated", ContractsClient.NamespaceSmartContractsCurrent,
298 Constants.PushChannels.Contracts, "Stanza", string.Empty, Content.ToString());
299
300 // Push Notification Rule, for Contract Deletion events when offline.
301
302 Content.Clear();
303 Content.Append("E:=GetElement(Stanza,'contractDeleted');");
304 Content.Append("{'myTitle':'");
305 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.ContractDeleted)]));
306 Content.Append("',");
307 Content.Append("'contractId':GetAttribute(E,'contractId'),");
308 Content.Append("'channelId':'");
309 Content.Append(Constants.PushChannels.Contracts);
310 Content.Append("',");
311 Content.Append("'content_available':true}");
312
313 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "contractDeleted", ContractsClient.NamespaceSmartContractsCurrent,
314 Constants.PushChannels.Contracts, "Stanza", string.Empty, Content.ToString());
315
316 // Push Notification Rule, for Contract Proposal events when offline.
317
318 Content.Clear();
319 Content.Append("E:=GetElement(Stanza,'contractProposal');");
320 Content.Append("{'myTitle':'");
321 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.ContractProposed)]));
322 Content.Append("',");
323 Content.Append("'myBody':GetAttribute(E,'message'),");
324 Content.Append("'contractId':Num(GetAttribute(E,'contractId')),");
325 Content.Append("'role':Num(GetAttribute(E,'role')),");
326 Content.Append("'channelId':'");
327 Content.Append(Constants.PushChannels.Contracts);
328 Content.Append("',");
329 Content.Append("'content_available':true}");
330
331 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "contractProposal", ContractsClient.NamespaceSmartContractsCurrent,
332 Constants.PushChannels.Contracts, "Stanza", string.Empty, Content.ToString());
333
334 #endregion
335
336 #region eDaler
337
338 // Push Notification Rule, for eDaler balance updates when offline.
339
340 Content.Clear();
341 Content.Append("E:=GetElement(Stanza,'balance');");
342 Content.Append("{'myTitle':'");
343 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.BalanceUpdated)]));
344 Content.Append("',");
345 Content.Append("'amount':Num(GetAttribute(E,'amount')),");
346 Content.Append("'currency':GetAttribute(E,'currency'),");
347 Content.Append("'timestamp':DateTime(GetAttribute(E,'timestamp')),");
348 Content.Append("'channelId':'");
349 Content.Append(Constants.PushChannels.EDaler);
350 Content.Append("',");
351 Content.Append("'content_available':true}");
352
353 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "balance", EDalerClient.NamespaceEDaler,
354 Constants.PushChannels.EDaler, "Stanza", string.Empty, Content.ToString());
355
356 #endregion
357
358 #region Neuro-Features
359
360 // Push Notification Rule, for token additions when offline.
361
362 Content.Clear();
363 Content.Append("E:=GetElement(Stanza,'tokenAdded');");
364 Content.Append("E2:=GetElement(E,'token');");
365 Content.Append("{'myTitle':'");
366 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.TokenAdded)]));
367 Content.Append("',");
368 Content.Append("'myBody':GetAttribute(E2,'friendlyName'),");
369 Content.Append("'value':Num(GetAttribute(E2,'value')),");
370 Content.Append("'currency':GetAttribute(E2,'currency'),");
371 Content.Append("'channelId':'");
372 Content.Append(Constants.PushChannels.Tokens);
373 Content.Append("',");
374 Content.Append("'content_available':true}");
375
376 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "tokenAdded", NeuroFeaturesClient.NamespaceNeuroFeatures,
377 Constants.PushChannels.Tokens, "Stanza", string.Empty, Content.ToString());
378
379 // Push Notification Rule, for token removals when offline.
380
381 Content.Clear();
382 Content.Append("E:=GetElement(Stanza,'tokenRemoved');");
383 Content.Append("E2:=GetElement(E,'token');");
384 Content.Append("{'myTitle':'");
385 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.TokenRemoved)]));
386 Content.Append("',");
387 Content.Append("'myBody':GetAttribute(E2,'friendlyName'),");
388 Content.Append("'value':Num(GetAttribute(E2,'value')),");
389 Content.Append("'currency':GetAttribute(E2,'currency'),");
390 Content.Append("'channelId':'");
391 Content.Append(Constants.PushChannels.Tokens);
392 Content.Append("',");
393 Content.Append("'content_available':true}");
394
395 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "tokenRemoved", NeuroFeaturesClient.NamespaceNeuroFeatures,
396 Constants.PushChannels.Tokens, "Stanza", string.Empty, Content.ToString());
397
398 #endregion
399
400 #region Provisioning
401
402 // Push Notification Rule, for friendship requests from things when offline.
403
404 Content.Clear();
405 Content.Append("ToJid:=GetAttribute(Stanza,'to');");
406 Content.Append("E:=GetElement(Stanza,'isFriend');");
407 Content.Append("RemoteJid:=GetAttribute(E,'remoteJid');");
408 Content.Append("{'myTitle':'");
409 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.AccessRequest)]));
410 Content.Append("',");
411 Content.Append("'myBody':RosterName(ToJid,RemoteJid),");
412 Content.Append("'remoteJid':RemoteJid,");
413 Content.Append("'jid':GetAttribute(E,'jid'),");
414 Content.Append("'key':GetAttribute(E,'key'),");
415 Content.Append("'q':'isFriend',");
416 Content.Append("'channelId':'");
417 Content.Append(Constants.PushChannels.Provisioning);
418 Content.Append("',");
419 Content.Append("'content_available':true}");
420
421 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "isFriend", ProvisioningClient.NamespaceProvisioningOwnerCurrent,
422 Constants.PushChannels.Provisioning, "Stanza", string.Empty, Content.ToString());
423
424 // Push Notification Rule, for readout requests from things when offline.
425
426 Content.Clear();
427 Content.Append("ToJid:=GetAttribute(Stanza,'to');");
428 Content.Append("E:=GetElement(Stanza,'canRead');");
429 Content.Append("RemoteJid:=GetAttribute(E,'remoteJid');");
430 Content.Append("{'myTitle':'");
431 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.ReadRequest)]));
432 Content.Append("',");
433 Content.Append("'myBody':RosterName(ToJid,RemoteJid),");
434 Content.Append("'remoteJid':RemoteJid,");
435 Content.Append("'jid':GetAttribute(E,'jid'),");
436 Content.Append("'key':GetAttribute(E,'key'),");
437 Content.Append("'q':'canRead',");
438 Content.Append("'channelId':'");
439 Content.Append(Constants.PushChannels.Provisioning);
440 Content.Append("',");
441 Content.Append("'content_available':true}");
442
443 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "canRead", ProvisioningClient.NamespaceProvisioningOwnerCurrent,
444 Constants.PushChannels.Provisioning, "Stanza", string.Empty, Content.ToString());
445
446 // Push Notification Rule, for control requests from things when offline.
447
448 Content.Clear();
449 Content.Append("ToJid:=GetAttribute(Stanza,'to');");
450 Content.Append("E:=GetElement(Stanza,'canControl');");
451 Content.Append("RemoteJid:=GetAttribute(E,'remoteJid');");
452 Content.Append("{'myTitle':'");
453 Content.Append(JSON.Encode(ServiceRef.Localizer[nameof(AppResources.ControlRequest)]));
454 Content.Append("',");
455 Content.Append("'myBody':RosterName(ToJid,RemoteJid),");
456 Content.Append("'remoteJid':RemoteJid,");
457 Content.Append("'jid':GetAttribute(E,'jid'),");
458 Content.Append("'key':GetAttribute(E,'key'),");
459 Content.Append("'q':'canControl',");
460 Content.Append("'channelId':'");
461 Content.Append(Constants.PushChannels.Provisioning);
462 Content.Append("',");
463 Content.Append("'content_available':true}");
464
465 await ServiceRef.XmppService.AddPushNotificationRule(MessageType.Normal, "canControl", ProvisioningClient.NamespaceProvisioningOwnerCurrent,
466 Constants.PushChannels.Provisioning, "Stanza", string.Empty, Content.ToString());
467
468 #endregion
469
471 }
472 }
473 }
474 catch (Exception ex)
475 {
476 ServiceRef.LogService.LogException(ex);
477 }
478 }
479 }
480}
eDaler XMPP client.
Definition: EDalerClient.cs:24
const string NamespaceEDaler
Namespace of eDaler component.
Definition: EDalerClient.cs:28
const string Provisioning
Provisioning channel
Definition: Constants.cs:627
const string Petitions
Petitions channel
Definition: Constants.cs:602
const string Identities
Identities channel
Definition: Constants.cs:607
const string Messages
Messages channel
Definition: Constants.cs:597
const string EDaler
eDaler channel
Definition: Constants.cs:617
const string Tokens
Tokens channel
Definition: Constants.cs:622
const string Contracts
Contracts channel
Definition: Constants.cs:612
Runtime setting key names.
Definition: Constants.cs:877
const string PushNotificationConfigurationVersion
Push-notification configuration version.
Definition: Constants.cs:881
const string PushNotificationReportDate
When push-notification token was reported.
Definition: Constants.cs:886
const string PushNotificationToken
Push-notification token.
Definition: Constants.cs:891
A set of never changing property constants and helpful values.
Definition: Constants.cs:7
async Task CheckPushNotificationToken(TokenInformation? TokenInformation)
Checks if the Push Notification Token is current and registered properly.
bool TryGetToken(PushMessagingService Source, out string? Token)
Tries to get a token from a push notification service.
TokenEventHandler? OnNewToken
Event raised when a new token is made available.
async Task NewToken(TokenInformation TokenInformation)
New token received from push notification back-end.
Event argumens for token-based events.
Contains information about a push notification token.
PushMessagingService Service
Service issuing the token
Base class that references services in the app.
Definition: ServiceRef.cs:31
static ILogService LogService
Log service.
Definition: ServiceRef.cs:91
static IStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:235
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:67
const string NamespaceNeuroFeatures
Namespace for Neuro-Features.
Neuro-Feature Token
Definition: Token.cs:43
Helps with common JSON-related tasks.
Definition: JSON.cs:14
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:507
Adds support for legal identities, smart contracts and signatures to an XMPP client.
const string NamespaceSmartContractsCurrent
Current namespce for smart contracts.
const string NamespaceLegalIdentitiesCurrent
Current namespace for legal identities.
Implements an XMPP provisioning client interface.
const string NamespaceProvisioningOwnerCurrent
Current namespace for provisioning by owners.
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
delegate Task TokenEventHandler(object? Sender, TokenEventArgs e)
Delegate for token event handlers.
ClientType
Type of client requesting notification.
Definition: ClientType.cs:7
PushMessagingService
Push messaging service used.
MessageType
Type of message received.
Definition: MessageType.cs:7