@atmosx/event-product-parser 3.0.3 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +1 -1
  2. package/.github/README.md +163 -0
  3. package/.github/SUPPORTED_EVENTS.md +370 -0
  4. package/.github/docs/docs-functions.md +30 -0
  5. package/.github/docs/docs-general-configurations.md +209 -0
  6. package/.github/docs/docs-integrations.md +37 -0
  7. package/.github/docs/docs-listeners.md +28 -0
  8. package/.github/docs/docs-source-configuration.md +218 -0
  9. package/.github/docs/docs-technical.md +50 -0
  10. package/.github/logo.png +0 -0
  11. package/.github/samples/SAMPLE_EAS_GENERATION.wav +0 -0
  12. package/.github/samples/SAMPLE_GEOJSON.json +204 -0
  13. package/.github/samples/SAMPLE_TEXT_PRODUCT.txt +119 -0
  14. package/dist/cjs/index.cjs +4681 -13425
  15. package/dist/esm/index.mjs +3163 -11909
  16. package/package.json +4 -8
  17. package/src/@building/{building.create.ts → CreateEvent.ts} +25 -11
  18. package/src/@building/{building.clean.ts → GetCleanedEvent.ts} +2 -2
  19. package/src/@building/GetEventAttachments.ts +86 -0
  20. package/src/@building/{building.enhance.ts → GetEventEnhancedName.ts} +11 -12
  21. package/src/@building/{building.geometry.ts → GetEventGeometry.ts} +7 -7
  22. package/src/@building/{building.headers.ts → GetEventHeader.ts} +3 -3
  23. package/src/@building/{building.polygon.ts → GetEventNodes.ts} +13 -11
  24. package/src/@building/{building.office.ts → GetEventOffice.ts} +6 -6
  25. package/src/@building/GetEventProperties.ts +96 -0
  26. package/src/@building/{building.signature.ts → GetEventSignature.ts} +14 -15
  27. package/src/@building/{building.tags.ts → GetEventTags.ts} +9 -4
  28. package/src/@building/{building.tracking.ts → GetEventTracking.ts} +15 -15
  29. package/src/@building/{building.validate.ts → ValidateEvents.ts} +67 -60
  30. package/src/@core/{core.clearEvents.ts → ClearEvents.ts} +3 -4
  31. package/src/@core/{core.createListener.ts → CreateListener.ts} +3 -3
  32. package/src/@core/{core.getEvents.ts → GetEvents.ts} +1 -2
  33. package/src/@core/{core.getNodes.ts → GetNodes.ts} +1 -2
  34. package/src/@core/{core.getRandomEvent.ts → GetRandomEvent.ts} +1 -2
  35. package/src/@core/{core.getVersion.ts → GetVersion.ts} +1 -2
  36. package/src/@core/ManualEvent.ts +56 -0
  37. package/src/@core/QueryStanza.ts +39 -0
  38. package/src/@core/{core.setNode.ts → SetNode.ts} +8 -8
  39. package/src/@core/{core.start.ts → StartService.ts} +21 -21
  40. package/src/@core/{core.stop.ts → StopService.ts} +1 -1
  41. package/src/@enums/AWIPS.ts +366 -0
  42. package/src/{@dictionaries/dictionaries.eventActions.ts → @enums/Actions.ts} +1 -1
  43. package/src/{@dictionaries/dictionaries.eventCancelMessages.ts → @enums/Cancellation.ts} +2 -1
  44. package/src/{@dictionaries/dictionaries.eventCauses.ts → @enums/Causes.ts} +1 -1
  45. package/src/{@dictionaries/dictionaries.statusCorrelationText.ts → @enums/Correlations.ts} +1 -1
  46. package/src/@enums/Enhanced.ts +70 -0
  47. package/src/{@dictionaries/dictionaries.eventTypes.ts → @enums/Events.ts} +1 -1
  48. package/src/{@dictionaries/dictionaries.regExp.ts → @enums/Expressions.ts} +2 -1
  49. package/src/@enums/GlobalFilter.ts +24 -0
  50. package/src/{@dictionaries/dictionaries.hailStrings.ts → @enums/Hail.ts} +7 -2
  51. package/src/{@dictionaries/dictionaries.officeICAOs.ts → @enums/ICAO.ts} +3 -1
  52. package/src/@enums/Matches.ts +55 -0
  53. package/src/{@dictionaries/dictionaries.eventProducts.ts → @enums/Products.ts} +1 -1
  54. package/src/{@dictionaries/dictionaries.eventRecords.ts → @enums/Records.ts} +1 -1
  55. package/src/{@dictionaries/dictionaries.eventSeverity.ts → @enums/Severity.ts} +1 -1
  56. package/src/{@dictionaries/dictionaries.shapefileLinks.ts → @enums/Shapefiles.ts} +1 -1
  57. package/src/@enums/States.ts +72 -0
  58. package/src/{@dictionaries/dictionaries.eventStatus.ts → @enums/Status.ts} +1 -1
  59. package/src/@enums/Strings.ts +22 -0
  60. package/src/@enums/Tags.ts +167 -0
  61. package/src/{@dictionaries/dictionaries.testSignatures.ts → @enums/Testing.ts} +3 -2
  62. package/src/@events/{events.api.ts → ParseAPI.ts} +42 -38
  63. package/src/@events/{events.text.ts → ParseText.ts} +25 -18
  64. package/src/@events/{events.ugc.ts → ParseUGC.ts} +28 -19
  65. package/src/@events/{events.vtec.ts → ParseVTEC.ts} +21 -21
  66. package/src/@manager/CreateActions.ts +213 -0
  67. package/src/@manager/{manager.mkEvent.ts → MakeEvent.ts} +34 -30
  68. package/src/@manager/RemoveEvent.ts +70 -0
  69. package/src/@manager/{manager.setHash.ts → SetHash.ts} +8 -9
  70. package/src/@manager/{manager.updateEvents.ts → UpdateEvents.ts} +9 -9
  71. package/src/@manager/{manager.updateNodes.ts → UpdateNode.ts} +24 -17
  72. package/src/@modules/@database/{database.cache.ts → GetCachedEvents.ts} +10 -8
  73. package/src/@modules/@database/ImportBroadcastify.ts +70 -0
  74. package/src/@modules/@database/{database.shapefiles.ts → ImportShapefiles.ts} +17 -20
  75. package/src/@modules/@database/{database.stanza.ts → ImportStanza.ts} +5 -6
  76. package/src/@modules/@database/{database.init.ts → InitializeDatabase.ts} +25 -11
  77. package/src/@modules/@eas/GenerateEASMessage.ts +144 -0
  78. package/src/@modules/@eas/GetCleanDescription.ts +91 -0
  79. package/src/@modules/@eas/{eas.getFloatPCM16.ts → GetFloatPCM16.ts} +1 -1
  80. package/src/@modules/@eas/{eas.getMergedPCM16.ts → GetMergedPCM16.ts} +1 -1
  81. package/src/@modules/@eas/{eas.getPCM16.ts → GetPCM16.ts} +1 -1
  82. package/src/@modules/@eas/{eas.getPCMToFloat.ts → GetPCMToFloat.ts} +1 -1
  83. package/src/@modules/@eas/{eas.getSampledPCM16.ts → GetSampledPCM16.ts} +1 -1
  84. package/src/@modules/@eas/GetTTS.ts +54 -0
  85. package/src/@modules/@eas/{eas.getWavPCM16.ts → GetWavPCM16.ts} +1 -1
  86. package/src/@modules/@eas/{eas.setAFSK.ts → SetAFSK.ts} +1 -2
  87. package/src/@modules/@eas/{eas.setAsciiToBits.ts → SetAsciiToBits.ts} +1 -2
  88. package/src/@modules/@eas/{eas.setAttentionTone.ts → SetAttentionTone.ts} +1 -1
  89. package/src/@modules/@eas/{eas.setNoise.ts → SetNoise.ts} +6 -5
  90. package/src/@modules/@eas/{eas.setRadioEffect.ts → SetRadioEffect.ts} +6 -5
  91. package/src/@modules/@eas/{eas.setSameHeader.ts → SetSameHeader.ts} +7 -8
  92. package/src/@modules/@stanza/{stanza.getAwipsType.ts → GetAwipsType.ts} +11 -9
  93. package/src/@modules/@stanza/{stanza.validate.ts → ValidateStanza.ts} +9 -9
  94. package/src/@modules/@utilities/CreateHttp.ts +83 -0
  95. package/src/@modules/@utilities/{utilities.getFormattedTime.ts → GetFormattedTime.ts} +1 -1
  96. package/src/@modules/@utilities/GetLatestIssuance.ts +32 -0
  97. package/src/@modules/@utilities/GetMatched.ts +33 -0
  98. package/src/@modules/@utilities/{utilities.getSettings.ts → GetSettings.ts} +2 -2
  99. package/src/@modules/@utilities/{utilities.getShapeNearestPoint.ts → GetShapeNearestPoint.ts} +1 -1
  100. package/src/@modules/@utilities/{utilities.setCronSchedule.ts → SetCronSchedule.ts} +41 -12
  101. package/src/{@types/types.webhook.ts → @modules/@utilities/SetDebug.ts} +16 -6
  102. package/src/@modules/@utilities/{utilities.setEventEmit.ts → SetEventEmit.ts} +5 -5
  103. package/src/@modules/@utilities/{utilities.setListener.ts → SetListener.ts} +1 -1
  104. package/src/@modules/@utilities/{utilities.setSettings.ts → SetSettings.ts} +2 -2
  105. package/src/@modules/@utilities/{utilities.setSleep.ts → SetSleep.ts} +1 -1
  106. package/src/@modules/@utilities/{utilities.setTimeoutAction.ts → SetTimeoutAction.ts} +8 -3
  107. package/src/@modules/@utilities/{utilities.setWarning.ts → SetWarning.ts} +4 -4
  108. package/src/@modules/@xmpp/{xmpp.xDeploy.ts → DeployXMPP.ts} +13 -14
  109. package/src/@modules/@xmpp/{xmpp.xError.ts → ErrorXMPP.ts} +1 -3
  110. package/src/@modules/@xmpp/{xmpp.xOffline.ts → OfflineXMPP.ts} +3 -4
  111. package/src/@modules/@xmpp/{xmpp.xOnline.ts → OnlineXMPP.ts} +6 -5
  112. package/src/@modules/@xmpp/{xmpp.xReconnect.ts → ReconnectXMPP.ts} +8 -8
  113. package/src/@modules/@xmpp/{xmpp.xStanza.ts → StanzaXMPP.ts} +11 -11
  114. package/src/@parsers/@hvtec/{hvtec.extract.ts → HVExtract.ts} +10 -10
  115. package/src/@parsers/@pvtec/{pvtec.expires.ts → GetExpiry.ts} +1 -1
  116. package/src/@parsers/@pvtec/{pvtec.extract.ts → VTECExtract.ts} +17 -17
  117. package/src/@parsers/@text/{text.getDescriptionFromProduct.ts → GetDescriptionFromProduct.ts} +12 -5
  118. package/src/@parsers/@text/GetEmbededText.ts +56 -0
  119. package/src/@parsers/@text/{text.getPolygonFromProduct.ts → GetPolygonFromProduct.ts} +31 -10
  120. package/src/@parsers/@text/GetStringText.ts +58 -0
  121. package/src/@parsers/@text/{text.getTextFromProduct.ts → GetTextFromProduct.ts} +1 -1
  122. package/src/@parsers/@text/{text.getXML.ts → GetXML.ts} +1 -1
  123. package/src/@parsers/@ugc/GetCache.ts +24 -0
  124. package/src/@parsers/@ugc/{ugc.expiry.ts → GetExpiry.ts} +1 -1
  125. package/src/@parsers/@ugc/{ugc.locations.ts → GetLocations.ts} +35 -7
  126. package/src/@parsers/@ugc/{ugc.header.ts → GetUGCHeader.ts} +3 -3
  127. package/src/@parsers/@ugc/{ugc.coordinates.ts → GetZonePolygon.ts} +4 -3
  128. package/src/@parsers/@ugc/SetCache.ts +28 -0
  129. package/src/@parsers/@ugc/{ugc.extract.ts → UGCExtract.ts} +6 -6
  130. package/src/@types/Actions.ts +37 -0
  131. package/src/@types/{type.event.ts → Event.ts} +7 -5
  132. package/src/@types/{type.properties.ts → Properties.ts} +11 -8
  133. package/src/@types/{types.settings.ts → Settings.ts} +31 -7
  134. package/src/@types/{types.compiled.ts → StanzaCompiled.ts} +2 -1
  135. package/src/bootstrap.ts +33 -9
  136. package/src/index.ts +28 -26
  137. package/tsup.config.ts +1 -1
  138. package/README.md +0 -339
  139. package/dist/index.d.mts +0 -239
  140. package/dist/index.d.ts +0 -239
  141. package/src/@building/building.properties.ts +0 -89
  142. package/src/@dictionaries/dictionaries.betterEventNames.ts +0 -99
  143. package/src/@dictionaries/dictionaries.eventAwipAbreviations.ts +0 -364
  144. package/src/@dictionaries/dictionaries.eventTags.ts +0 -93
  145. package/src/@dictionaries/dictionaries.eventsMatchText.ts +0 -36
  146. package/src/@dictionaries/dictionaries.transcribedMessageReplacements.ts +0 -68
  147. package/src/@manager/manager.rmEvent.ts +0 -49
  148. package/src/@manager/manager.updateWebhooks.ts +0 -50
  149. package/src/@modules/@eas/eas.setEasTone.ts +0 -137
  150. package/src/@modules/@utilities/utilities.createHttp.ts +0 -85
  151. package/src/@modules/@utilities/utilities.createWebhook.ts +0 -115
  152. package/test.js +0 -91
  153. /package/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
  154. /package/{LICENSE → .github/LICENSE} +0 -0
  155. /package/{SECURITY.md → .github/SECURITY.md} +0 -0
  156. /package/src/@parsers/@ugc/{ugc.zones.ts → GetZones.ts} +0 -0
  157. /package/src/@types/{types.attributes.ts → Attributes.ts} +0 -0
  158. /package/src/@types/{types.hvtec.ts → HVTEC.ts} +0 -0
  159. /package/src/@types/{types.hash.ts → Hash.ts} +0 -0
  160. /package/src/@types/{types.stanza.ts → Stanza.ts} +0 -0
  161. /package/src/@types/{types.ugc.ts → UGC.ts} +0 -0
  162. /package/src/@types/{types.pvtec.ts → VTEC.ts} +0 -0
@@ -17,21 +17,20 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeAttributes } from "../@types/types.attributes";
21
- import { TypeStanzaCompiled } from "../@types/types.compiled"
22
- import { TypeEvent } from "../@types/type.event";
23
- import { TypePVTEC } from "../@types/types.pvtec";
24
- import { TypeHVTEC } from "../@types/types.hvtec";
25
- import { pvExtract } from "../@parsers/@pvtec/pvtec.extract";
26
- import { hvExtract } from "../@parsers/@hvtec/hvtec.extract";
27
- import { ugcExtract } from "../@parsers/@ugc/ugc.extract";
28
- import { properties } from "../@building/building.properties";
29
- import { getEventHeader } from "../@building/building.headers";
30
- import { getEventTracking } from "../@building/building.tracking";
31
- import { validateEvents } from "../@building/building.validate";
20
+ import { TypeAttributes } from "../@types/Attributes";
21
+ import { TypeStanzaCompiled } from "../@types/StanzaCompiled"
22
+ import { TypePVTEC } from "../@types/VTEC";
23
+ import { TypeHVTEC } from "../@types/HVTEC";
24
+ import { bootstrap } from "../bootstrap";
25
+ import { VTECExtract } from "../@parsers/@pvtec/VTECExtract";
26
+ import { HVExtract } from "../@parsers/@hvtec/HVExtract";
27
+ import { ugcExtract } from "../@parsers/@ugc/UGCExtract";
28
+ import { GetEventProperties } from "../@building/GetEventProperties";
29
+ import { GetEventHeader } from "../@building/GetEventHeader";
30
+ import { GetEventTracking } from "../@building/GetEventTracking";
31
+ import { SetDebug } from "../@modules/@utilities/SetDebug";
32
32
 
33
- export const vtec = async (stanza: TypeStanzaCompiled): Promise<void> => {
34
- let processed: TypeEvent[] = [];
33
+ export const ParseVTEC = async (stanza: TypeStanzaCompiled): Promise<void> => {
35
34
  const getMessages = stanza?.message
36
35
  ?.split(/(?=\$\$)/g)
37
36
  ?.map(message => message.trim())
@@ -40,17 +39,17 @@ export const vtec = async (stanza: TypeStanzaCompiled): Promise<void> => {
40
39
  for (const message of getMessages) {
41
40
  const tick = performance.now();
42
41
  const attributes = stanza?.attributes as TypeAttributes
43
- const pVtec = pvExtract(message) as TypePVTEC[];
44
- const hVtec = hvExtract(message) as TypeHVTEC[];
42
+ const pVtec = VTECExtract(message) as TypePVTEC[];
43
+ const hVtec = HVExtract(message) as TypeHVTEC[];
45
44
  const ugc = await ugcExtract(message)
46
45
  if (pVtec != null && ugc != null ) {
47
46
  for (const pv of pVtec) {
48
47
  const vtec = pv;
49
- const props = properties({ message, attributes, ugc, pVtec: vtec })
50
- const header = getEventHeader({properties: props, getType: stanza.getType, vtec: vtec})
48
+ const props = GetEventProperties({ message, attributes, ugc, pVtec: vtec })
49
+ const header = GetEventHeader({properties: props, getType: stanza.getType, vtec: vtec})
51
50
  const issued = new Date(attributes.issue)?? new Date()
52
51
  const expires = new Date(vtec.expires)
53
- processed.push({
52
+ bootstrap.cache.processed.push({
54
53
  type: `Feature`,
55
54
  geometry: {
56
55
  type: `Point`,
@@ -66,10 +65,11 @@ export const vtec = async (stanza: TypeStanzaCompiled): Promise<void> => {
66
65
  metadata: {
67
66
  ms: performance.now() - tick,
68
67
  source: `events.vtec`,
69
- tracking: getEventTracking({ type: `VTEC`, stanza, attributes, properties: props, vtec }),
68
+ tracking: GetEventTracking({ type: `VTEC`, stanza, attributes, properties: props, vtec }),
70
69
  header: header,
71
70
  vtec: pv,
72
71
  hvtec: hVtec,
72
+ raw: message,
73
73
  history: [
74
74
  {
75
75
  description: props.description,
@@ -80,8 +80,8 @@ export const vtec = async (stanza: TypeStanzaCompiled): Promise<void> => {
80
80
  }
81
81
  }
82
82
  })
83
+ SetDebug({ title: `ParseVTEC`, message: `${Math.round(performance.now() - tick)}ms` })
83
84
  }
84
85
  }
85
86
  }
86
- validateEvents(processed)
87
87
  }
@@ -0,0 +1,213 @@
1
+ /*
2
+ _ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
12
+ Discord: https://atmosphericx-discord.scriptkitty.cafe
13
+ Ko-Fi: https://ko-fi.com/k3yomi
14
+ Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Internal Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { TypeSettings } from "../@types/Settings";
21
+ import { TypeEvent } from "../@types/Event";
22
+ import { TypeActions } from "../@types/Actions";
23
+ import { bootstrap } from "../bootstrap"
24
+ import { GenerateEASMessage } from "../@modules/@eas/GenerateEASMessage"
25
+ import { SetTimeoutAction } from "../@modules/@utilities/SetTimeoutAction";
26
+ import { GetEmebedText } from "../@parsers/@text/GetEmbededText";
27
+ import { GetStringText } from "../@parsers/@text/GetStringText";
28
+ import { GetCleanedEvent } from "../@building/GetCleanedEvent"
29
+ import { CreateHttp } from "../@modules/@utilities/CreateHttp";
30
+ import { SetDebug } from "../@modules/@utilities/SetDebug";
31
+ import { GetMatched } from "../@modules/@utilities/GetMatched";
32
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from "fs";
33
+
34
+
35
+ export const CreateActions = async (event: TypeEvent): Promise<void> => {
36
+ const tick = performance.now()
37
+ const settings = bootstrap.settings as TypeSettings;
38
+ const notifyServer = settings.NotifyServer;
39
+ const actions = settings.ActionSettings as TypeActions[];
40
+ const properties = event.properties;
41
+ const hasActions = Array.isArray(actions) && actions.length > 0;
42
+ if (!hasActions) {
43
+ SetDebug({ title: `CreateActions`, message: `${Math.round(performance.now() - tick)}ms` });
44
+ return;
45
+ }
46
+ const metadata = {
47
+ text: null,
48
+ eas: null,
49
+ json: null,
50
+ name: properties.event,
51
+ status: properties.status,
52
+ description: properties.description,
53
+ tracking: properties.metadata.tracking,
54
+ header: properties.metadata.header,
55
+ raw: properties.metadata.raw,
56
+ expired: properties.status_metadata.is_expired,
57
+ attachments: properties.metadata.attachments
58
+ }
59
+
60
+ for (const action of actions) {
61
+ const events = action?.Events;
62
+ const webhook = action?.Webhook;
63
+ const notify = action?.NotificationServer;
64
+ const uploads = action?.Uploads
65
+ const isMatched = GetMatched(events ?? [], metadata.name)
66
+
67
+ if (events?.length == 0 || isMatched) {
68
+ if (uploads?.EAS) {
69
+ metadata.eas = await GenerateEASMessage({
70
+ title: `${metadata.name}_${metadata.status}_${metadata.tracking}`,
71
+ message: metadata.description,
72
+ header: metadata.header
73
+ });
74
+ }
75
+
76
+ if (uploads?.TEXT) {
77
+ const fDestination = settings.GlobalSettings.ArchiveSettings.TextDirectory;
78
+ if (fDestination) {
79
+ const file = (`${fDestination}/${metadata.name}_${metadata.tracking}.txt`).replace(/[\\:*?"<>|]/g, "_").replace(/\s+/g, " ").trim();
80
+ if (!existsSync(fDestination)) { mkdirSync(fDestination, { recursive: true }); }
81
+ if (existsSync(file)) {
82
+ appendFileSync(file, `${(`\n`).repeat(10)}${metadata.raw}`);
83
+ } else {
84
+ writeFileSync(file, metadata.raw);
85
+ }
86
+ }
87
+ metadata.text = metadata.raw;
88
+ }
89
+
90
+ if (uploads?.JSON) {
91
+ const eDestination = settings.GlobalSettings.ArchiveSettings.JSONDirectory;
92
+ if (eDestination) {
93
+ const file = (`${eDestination}/${metadata.name}_${metadata.tracking}.json`).replace(/[\\:*?"<>|]/g, "_").replace(/\s+/g, " ").trim();
94
+ if (!existsSync(eDestination)) { mkdirSync(eDestination, { recursive: true }); }
95
+ writeFileSync(file, JSON.stringify(GetCleanedEvent(event), null, 2));
96
+ }
97
+ metadata.json = JSON.stringify(GetCleanedEvent(event), null, 2);
98
+ }
99
+
100
+ if (notifyServer?.Enabled && notify?.Enabled && notify?.Topic) {
101
+ let buttons = [];
102
+ const auth = notifyServer?.Credentials?.Username && notifyServer?.Credentials?.Password ? { username: notifyServer?.Credentials?.Username, password: notifyServer?.Credentials?.Password } : undefined;
103
+ if (notifyServer?.MediaStorage?.EAS) {
104
+ if (metadata.eas) {
105
+ buttons.push({
106
+ "action": "view",
107
+ "label": "Listen",
108
+ "url": `${notifyServer?.MediaStorage?.EAS}/${metadata.name}_${metadata.status}_${metadata.tracking}.wav`,
109
+ })
110
+ }
111
+ }
112
+ if (metadata.json) {
113
+ buttons.push({
114
+ "action": "view",
115
+ "label": "View JSON",
116
+ "url": `${notifyServer?.MediaStorage?.JSON}/${metadata.name}_${metadata.tracking}.json`,
117
+ })
118
+ }
119
+ if (metadata.text) {
120
+ buttons.push({
121
+ "action": "view",
122
+ "label": "View Text",
123
+ "url": `${notifyServer?.MediaStorage?.TEXT}/${metadata.name}_${metadata.tracking}.txt`,
124
+ })
125
+ }
126
+ if (metadata.attachments?.length > 0 && metadata.attachments.find(a => a.name === "Image: Graphic")) {
127
+ buttons.push({
128
+ "action": "view",
129
+ "label": "View Image",
130
+ "url": metadata.attachments.find(a => a.name === "Image: Graphic").link,
131
+ })
132
+ }
133
+ await CreateHttp({
134
+ url: `${notifyServer?.Server?.replace(/\/$/, "")}/${notify.Topic}`,
135
+ timeout: 15_000,
136
+ method: "PUT",
137
+ ...(auth && { auth }),
138
+ headers: {
139
+ "Title": `${metadata.name} (${metadata.status})`,
140
+ "Tags": properties.parameters.tags?.join(",") ?? "N/A",
141
+ "Priority": notify?.Priority ?? "5",
142
+ ... (buttons.length > 0 && { "Actions": JSON.stringify(buttons) }),
143
+ },
144
+ body: GetStringText(event)
145
+ })
146
+ }
147
+
148
+ if (webhook?.Enabled && webhook?.Destination) {
149
+ const isRatelimited = SetTimeoutAction({ identifier: webhook.Destination, interval: (webhook.Ratelimit ?? 2) * 2, max: (webhook.Ratelimit ?? 2), addTime: true })
150
+ const form = new FormData();
151
+ const embed = {
152
+ title: `${metadata.name} (${metadata.status})`,
153
+ description: GetEmebedText(event),
154
+ fields: [],
155
+ color: 16711680,
156
+ timestamp: new Date().toISOString(),
157
+ footer: { text: webhook.Title ?? `AtmosphericX` }
158
+ };
159
+
160
+ if (isRatelimited.limited) { return }
161
+
162
+
163
+ if (metadata.description && !metadata.expired) {
164
+ if (metadata.description.length > 900) {
165
+ metadata.description = metadata.description.substring(0, 900) + "\n\n[Message truncated due to length]";
166
+ }
167
+ embed.fields.push({
168
+ name: "Description",
169
+ value: metadata.description ? '```' + '\n' + metadata.description.split('\n').map(l => l.trim()).filter(Boolean).join('\n') + '\n' + '```' : ""
170
+ });
171
+ }
172
+
173
+
174
+ if (metadata.attachments?.length > 0) {
175
+ metadata.attachments = metadata.attachments.slice(0, 5);
176
+ embed.fields.push({
177
+ name: "Attachments",
178
+ value: metadata.attachments.map(attachment => `- [${attachment.name.length > 45 ? attachment.name.substring(0, 45) + '...' : attachment.name}](${attachment.link})`).join('\n')
179
+ });
180
+ }
181
+
182
+ if (uploads?.TEXT) {
183
+ form.append("fUpload", new Blob([Buffer.from(metadata.text)], {type: "application/text"}), `${properties.event}_${properties.status}_${properties.metadata.tracking}.txt`)
184
+ }
185
+
186
+ if (uploads?.JSON) {
187
+ form.append("fUpload2", new Blob([Buffer.from(metadata.json)], {type: "application/json"}), `${properties.event}_${properties.status}_${properties.metadata.tracking}.json`)
188
+ }
189
+
190
+ if (uploads?.EAS) {
191
+ if (metadata.eas) {
192
+ const file = readFileSync(metadata.eas)
193
+ form.append("fEas", new Blob([Buffer.from(file)], { type: "audio/mpeg" }), `${properties.event}_${properties.status}_${properties.metadata.tracking}_eas.mp3`)
194
+ }
195
+ }
196
+
197
+ form.append("payload_json", JSON.stringify({
198
+ username: webhook.Title ?? "AtmosphericX",
199
+ content: webhook.Message ?? "",
200
+ embeds: [embed]
201
+ }));
202
+
203
+ await CreateHttp({
204
+ url: webhook.Destination,
205
+ timeout: 15e3,
206
+ method: `POST`,
207
+ body: form
208
+ })
209
+ }
210
+ }
211
+ }
212
+ SetDebug({ title: `CreateActions`, message: `${Math.round(performance.now() - tick)}ms` })
213
+ }
@@ -17,44 +17,47 @@
17
17
 
18
18
  */
19
19
 
20
- import { setEventEmit } from "../@modules/@utilities/utilities.setEventEmit";
21
- import { TypeEvent } from "../@types/type.event";
20
+ import { TypeEvent } from "../@types/Event";
21
+ import { TypeSettings } from "../@types/Settings";
22
+ import { EnumGlobalFilter } from "../@enums/GlobalFilter";
22
23
  import { bootstrap } from "../bootstrap"
23
- import { setHash } from "./manager.setHash";
24
- import { updateWebhooks } from "./manager.updateWebhooks";
25
- import { updateNode } from "./manager.updateNodes";
26
- import { TypeSettings } from "../@types/types.settings";
24
+ import { SetHash } from "./SetHash";
25
+ import { CreateActions } from "./CreateActions";
26
+ import { UpdateNode } from "./UpdateNode";
27
+ import { SetEventEmit } from "../@modules/@utilities/SetEventEmit";
28
+ import { SetTimeoutAction } from "../@modules/@utilities/SetTimeoutAction";
27
29
 
28
- export const mkEvent = async (event: TypeEvent): Promise<void> => {
30
+ export const MakeEvent = async (event: TypeEvent): Promise<void> => {
29
31
  const settings = bootstrap.settings as TypeSettings;
30
32
  const features = bootstrap.cache.events.features;
31
- const map = new Map<string, typeof features[0]>();
32
-
33
- for (const f of features) {
34
- const key = f?.properties?.metadata?.tracking;
35
- if (!key) continue;
36
- map.set(key, f);
37
- }
38
-
33
+
39
34
  const getHash = event.properties.metadata.hash;
40
35
  const getTracking = event.properties.metadata.tracking;
41
36
  const isEntry = bootstrap.cache.hashes?.find(hash => hash.tracking === getTracking)
42
37
  const isHashed = isEntry?.hashes?.includes(getHash) ?? false;
43
- const getFeature = map.get(getTracking);
44
-
38
+ const getFeature = features.find(feature => feature.properties.metadata.tracking === getTracking);
39
+
45
40
  if (isHashed || event.properties.status_metadata.is_expired) return
46
- await setHash(event, isEntry)
47
- const isFilteredLocation = await updateNode(event).then(() => event.properties.metadata.filtered_proximity);
48
- if (!isFilteredLocation && settings.GlobalSettings.EventFiltering.NodeLocationFiltering) { return }
41
+ SetHash(event, isEntry)
42
+
43
+ await UpdateNode(event);
44
+ const isNodeFiltering = settings.GlobalSettings.EventFiltering.NodeLocationFiltering
45
+ if (isNodeFiltering && !event.properties.metadata.filtered_proximity && !EnumGlobalFilter.includes(event.properties.event.toLowerCase())) {
46
+ return
47
+ }
48
+
49
+ const isRatelimited = SetTimeoutAction({ identifier: getTracking, interval: 1, max: 1, addTime: true })
50
+ if (!isRatelimited.limited) {
51
+ SetEventEmit({
52
+ event: `onEventStatus`,
53
+ metadata: {
54
+ type: getFeature ? `Updated` : `New`,
55
+ event: event
56
+ },
57
+ message: `[${getFeature ? 'Updated' : 'New'}] ${event.properties.event} (${event.properties.status}) (${event.properties.metadata.tracking})`
58
+ })
59
+ }
49
60
 
50
- setEventEmit({
51
- event: `onEventStatus`,
52
- metadata: {
53
- type: getFeature ? `Updated` : `New`,
54
- event: event
55
- },
56
- message: `[${getFeature ? 'Updated' : 'New'}] ${event.properties.event} (${event.properties.status}) (${event.properties.metadata.tracking})`
57
- })
58
61
  if (settings.GlobalSettings.EventManagement) {
59
62
  if (event.properties.status_metadata.is_issued || event.properties.status_metadata.is_updated) {
60
63
  if (getFeature) {
@@ -67,7 +70,7 @@ export const mkEvent = async (event: TypeEvent): Promise<void> => {
67
70
  const iLocations = event.properties?.locations?.split(";").map((l: string) => l.trim()) ?? [];
68
71
  const iUgc = event.properties?.geocode?.ugc ?? [];
69
72
 
70
- const mHistory = [...cHistory, ...iHistory].filter((v, i, a) => a.indexOf(v) === i);
73
+ const mHistory = [...cHistory, ...iHistory].filter((v, i, a) => a.indexOf(v) === i).filter((v, i, a) => a.findIndex(h => h.description === v.description && h.issued === v.issued) === i);
71
74
  const mLocations = [...cLocations, ...iLocations].filter((v, i, a) => a.indexOf(v) === i).join('; ');
72
75
  const mUgc = [...cUgc, ...iUgc].filter((v, i, a) => a.indexOf(v) === i);
73
76
 
@@ -86,10 +89,11 @@ export const mkEvent = async (event: TypeEvent): Promise<void> => {
86
89
  },
87
90
  }
88
91
  };
92
+ await CreateActions(bootstrap.cache.events.features[getIndex])
89
93
  } else {
90
94
  features.push(event)
95
+ await CreateActions(event)
91
96
  }
92
97
  }
93
98
  }
94
- updateWebhooks(event)
95
99
  }
@@ -0,0 +1,70 @@
1
+ /*
2
+ _ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
12
+ Discord: https://atmosphericx-discord.scriptkitty.cafe
13
+ Ko-Fi: https://ko-fi.com/k3yomi
14
+ Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Internal Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { TypeEvent } from "../@types/Event";
21
+ import { EnumStrings } from "../@enums/Strings";
22
+ import { bootstrap } from "../bootstrap"
23
+ import { SetEventEmit } from "../@modules/@utilities/SetEventEmit";
24
+ import { SetTimeoutAction } from "../@modules/@utilities/SetTimeoutAction";
25
+ import { CreateActions } from "./CreateActions";
26
+
27
+
28
+ export const RemoveEvent = async (event: TypeEvent, isTimeBasedExpiration: boolean):Promise<void> => {
29
+ const gTracking = event.properties.metadata.tracking;
30
+ const isTrackingEventLogged = bootstrap.cache.events.features
31
+ .find(f => f?.properties?.metadata?.tracking === gTracking);
32
+ const isStatement = event.properties.status_metadata.is_statement;
33
+
34
+ if (isTrackingEventLogged) {
35
+ event.properties.expires = new Date().toISOString();
36
+ event.properties.status = `Expired`;
37
+ event.properties.status_metadata.is_expired = true;
38
+
39
+ const description = isTimeBasedExpiration ? EnumStrings.cancellation
40
+ .replace(`{SENDER}`, event.properties.geocode.office.name)
41
+ .replace(`{EVENT}`, event.properties.event) : event.properties.description
42
+ event.properties.description = description;
43
+ event.properties.metadata.raw = isTimeBasedExpiration ? description : event.properties.metadata.raw;
44
+ event.properties.metadata.history.push({
45
+ description: isTimeBasedExpiration ? description : event.properties.description,
46
+ issued: event.properties.expires,
47
+ status: event.properties.status
48
+ })
49
+
50
+ bootstrap.cache.events.features
51
+ .splice(bootstrap.cache.events.features.indexOf(isTrackingEventLogged), 1);
52
+ bootstrap.cache.hashes = bootstrap.cache.hashes
53
+ .filter(hash => hash.tracking !== gTracking);
54
+
55
+ if (!isStatement) {
56
+ SetEventEmit({
57
+ event: `onEventStatus`,
58
+ metadata: { type: `Removed`, event: event },
59
+ message: `[Removed] ${event.properties.event} (${event.properties.status}) (${gTracking})`
60
+ })
61
+ await CreateActions(event)
62
+ }
63
+ SetTimeoutAction({ identifier: gTracking, expire: true })
64
+ }
65
+ SetEventEmit({
66
+ event: `onEventCache`,
67
+ metadata: bootstrap.cache.events,
68
+ limited: true
69
+ })
70
+ }
@@ -17,21 +17,20 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeEvent } from "../@types/type.event";
21
- import { TypeHash } from "../@types/types.hash"
20
+ import { TypeEvent } from "../@types/Event";
21
+ import { TypeHash } from "../@types/Hash"
22
22
  import { bootstrap } from "../bootstrap"
23
23
 
24
-
25
- export const setHash = async (event: TypeEvent, entry: TypeHash): Promise<void> => {
26
- if (entry) {
27
- entry.hashes.push(event.properties.metadata.hash);
28
- entry.expires = event.properties.expires;
29
- } else {
24
+ export const SetHash = (event: TypeEvent, entry: TypeHash): void => {
25
+ if (!entry) {
30
26
  bootstrap.cache.hashes.push({
31
27
  tracking: event.properties.metadata.tracking,
32
28
  hashes: [event.properties.metadata.hash],
33
29
  expires: event.properties.expires
34
30
  });
31
+ return;
35
32
  }
36
-
33
+
34
+ entry.hashes.push(event.properties.metadata.hash);
35
+ entry.expires = event.properties.expires;
37
36
  }
@@ -17,19 +17,19 @@
17
17
 
18
18
  */
19
19
 
20
- import { getFormattedTime } from "../@modules/@utilities/utilities.getFormattedTime";
21
- import { TypeEvent } from "../@types/type.event";
20
+ import { TypeEvent } from "../@types/Event";
22
21
  import { bootstrap } from "../bootstrap"
23
- import { rmEvent } from "./manager.rmEvent";
22
+ import { SetEventEmit } from "../@modules/@utilities/SetEventEmit";
23
+ import { RemoveEvent } from "./RemoveEvent";
24
24
 
25
-
26
- export const updateEvents = async (selectedEvent?: TypeEvent): Promise<void> => {
25
+ export const UpdateEvents = async (selectedEvent?: TypeEvent): Promise<void> => {
27
26
  const events = bootstrap.cache.events.features;
28
- async function update(evt: TypeEvent) {
29
- if (new Date(evt.properties.expires) < new Date()) {
30
- rmEvent(evt);
27
+ async function update(event: TypeEvent) {
28
+ if (new Date(event.properties.expires) < new Date()) {
29
+ SetEventEmit({ event: `onExpiredProduct`, metadata: event })
30
+ await RemoveEvent(event, true)
31
31
  }
32
32
  }
33
- if (!selectedEvent) { await Promise.all(events.map(async (evt) => { await update(evt) })) }
33
+ if (!selectedEvent) { await Promise.all(events.map(async (event) => { await update(event) })) }
34
34
  if (selectedEvent) { await update(selectedEvent) }
35
35
  }
@@ -17,34 +17,41 @@
17
17
 
18
18
  */
19
19
 
20
- import { getEventNodes } from "../@building/building.polygon";
21
- import { setEventEmit } from "../@modules/@utilities/utilities.setEventEmit";
22
- import { TypeEvent } from "../@types/type.event";
20
+ import { TypeEvent } from "../@types/Event";
23
21
  import { bootstrap } from "../bootstrap"
22
+ import { GetEventNodes } from "../@building/GetEventNodes";
23
+ import { SetEventEmit } from "../@modules/@utilities/SetEventEmit";
24
24
 
25
-
26
- export const updateNode = async (selectedEvent?: TypeEvent): Promise<void> => {
25
+ export const UpdateNode = async (selectedEvent?: TypeEvent): Promise<void> => {
27
26
  const events = bootstrap.cache.events.features;
28
27
  const ttl = bootstrap.settings.GlobalSettings.NodeTTL * 1e3;
29
28
  let total = 0;
30
29
 
31
- async function update(evt: TypeEvent) {
30
+ const TTLEvents = selectedEvent ? [selectedEvent] : events.filter((evt) => {
32
31
  const lastUpdate = evt?.properties?.metadata?.updated ?? null;
33
- if (lastUpdate != null && (Date.now() - lastUpdate) < ttl) {
34
- return evt;
35
- }
36
- const node = await getEventNodes(evt);
32
+ return lastUpdate == null || (Date.now() - lastUpdate) >= ttl;
33
+ });
34
+
35
+ if (TTLEvents.length === 0) return;
36
+
37
+ async function update(evt: TypeEvent) {
38
+ const node = await GetEventNodes(evt);
37
39
  if (node.nodes.length > 0) {
38
- total++
40
+ total++;
39
41
  }
40
- evt.properties.metadata.nodes = node.nodes
41
- evt.properties.metadata.filtered_proximity = node.filtered
42
- evt.properties.metadata.updated = node.updated
42
+ evt.properties.metadata.nodes = node.nodes;
43
+ evt.properties.metadata.filtered_proximity = node.filtered;
44
+ evt.properties.metadata.updated = node.updated;
45
+ }
46
+
47
+ const concurrency = 4;
48
+ for (let index = 0; index < TTLEvents.length; index += concurrency) {
49
+ const batch = TTLEvents.slice(index, index + concurrency);
50
+ await Promise.all(batch.map((evt) => update(evt)));
43
51
  }
44
- if (!selectedEvent) { await Promise.all(events.map(async (evt) => { await update(evt) })) }
45
- if (selectedEvent) { await update(selectedEvent) }
52
+
46
53
  if (total > 0) {
47
- setEventEmit({
54
+ SetEventEmit({
48
55
  event: `onNodeUpdate`,
49
56
  metadata: {
50
57
  type: `global-update`,
@@ -17,19 +17,20 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeSettings } from "../../@types/types.settings"
20
+ import { TypeSettings } from "../../@types/Settings"
21
21
  import { bootstrap } from '../../bootstrap'
22
- import { setWarning } from '../@utilities/utilities.setWarning';
23
- import { createEvent } from '../../@building/building.create';
22
+ import { SetWarning } from '../@utilities/SetWarning';
23
+ import { CreateEvent } from '../../@building/CreateEvent';
24
+ import { ValidateEvents } from "../../@building/ValidateEvents";
24
25
 
25
- export const getCachedEvents = async (): Promise<void> => {
26
+ export const GetCachedEvents = async (): Promise<void> => {
26
27
  try {
27
28
  const settings = bootstrap.settings as TypeSettings;
28
29
  const tick = performance.now();
29
30
  if (settings.NOAAWeatherWireServiceSettings.CacheSettings.Enabled) {
30
31
  const max = settings.NOAAWeatherWireServiceSettings.CacheSettings.MaxRetentionHistory ?? 500;
31
32
  const get = await bootstrap.database.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`).all(max) as { rowid: number; stanza: string }[];
32
- setWarning({ message: `Fetched ${get.length} cached events from the database in ${Math.floor(performance.now() - tick)} ms` })
33
+ SetWarning({ message: `Fetched ${get.length} cached stanzas from the database in ${Math.floor(performance.now() - tick)} ms` })
33
34
  let events = get.map((row) => JSON.parse(row.stanza))
34
35
  .filter(stanza => {
35
36
  if (!stanza) { return }
@@ -39,10 +40,11 @@ export const getCachedEvents = async (): Promise<void> => {
39
40
  return !isSkippable
40
41
  });
41
42
  events = events.sort((a, b) => b.issued - a.issued)
42
- await Promise.all(events.map(event => createEvent(event)))
43
- setWarning({ message: `Processed ${events.length} cached events in ${Math.floor(performance.now() - tick)} ms` })
43
+ await Promise.all(events.map(event => CreateEvent(event, true)))
44
+ SetWarning({ message: `Processed ${events.length} cached stanzas in ${Math.floor(performance.now() - tick)} ms` })
45
+ await ValidateEvents(bootstrap.cache.processed)
44
46
  }
45
47
  } catch (error) {
46
- setWarning({ message: `An error occurred while fetching cached events: ${error.message} -> ${error.stack}` })
48
+ SetWarning({ message: `An error occurred while fetching cached stanzas: ${error.message} -> ${error.stack}` })
47
49
  }
48
50
  }