@atmosx/event-product-parser 3.0.4 → 3.0.7

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 (212) hide show
  1. package/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +1 -1
  2. package/.github/SUPPORTED_EVENTS.md +370 -0
  3. package/.github/docs/docs-functions.md +30 -0
  4. package/.github/docs/docs-general-configurations.md +199 -0
  5. package/.github/docs/docs-integrations.md +38 -0
  6. package/.github/docs/docs-listeners.md +28 -0
  7. package/.github/docs/docs-source-configuration.md +218 -0
  8. package/.github/docs/docs-technical.md +50 -0
  9. package/.github/logo.png +0 -0
  10. package/.github/samples/SAMPLE_EAS_GENERATION.wav +0 -0
  11. package/.github/samples/SAMPLE_GEOJSON.json +204 -0
  12. package/.github/samples/SAMPLE_TEXT_PRODUCT.txt +119 -0
  13. package/README.md +157 -337
  14. package/dist/cjs/index.cjs +24125 -15815
  15. package/dist/esm/index.mjs +24144 -15839
  16. package/package.json +56 -53
  17. package/pnpm-workspace.yaml +4 -0
  18. package/src/bootstrap.ts +83 -43
  19. package/src/building/CreateEvent.ts +54 -0
  20. package/src/{@building/building.clean.ts → building/GetCleanedEvent.ts} +4 -4
  21. package/src/building/GetEventAttachments.ts +88 -0
  22. package/src/building/GetEventDirection.ts +28 -0
  23. package/src/{@building/building.enhance.ts → building/GetEventEnhancedName.ts} +13 -17
  24. package/src/{@building/building.geometry.ts → building/GetEventGeometry.ts} +19 -13
  25. package/src/{@building/building.headers.ts → building/GetEventHeader.ts} +13 -13
  26. package/src/{@building/building.polygon.ts → building/GetEventNodes.ts} +26 -22
  27. package/src/{@building/building.office.ts → building/GetEventOffice.ts} +14 -14
  28. package/src/building/GetEventProperties.ts +105 -0
  29. package/src/{@building/building.signature.ts → building/GetEventSignature.ts} +16 -17
  30. package/src/{@core/core.createListener.ts → building/GetEventTags.ts} +11 -6
  31. package/src/building/GetEventTheme.ts +26 -0
  32. package/src/building/GetEventTracking.ts +64 -0
  33. package/src/components/audio/GenerateAudioMessage.ts +149 -0
  34. package/src/components/audio/components/GetCleanDescription.ts +91 -0
  35. package/src/{@modules/@eas/eas.getFloatPCM16.ts → components/audio/components/GetFloatPCM16.ts} +3 -3
  36. package/src/{@modules/@eas/eas.getMergedPCM16.ts → components/audio/components/GetMergedPCM16.ts} +3 -3
  37. package/src/{@modules/@eas/eas.getPCM16.ts → components/audio/components/GetPCM16.ts} +13 -8
  38. package/src/{@modules/@eas/eas.getPCMToFloat.ts → components/audio/components/GetPCMToFloat.ts} +3 -3
  39. package/src/{@modules/@eas/eas.getSampledPCM16.ts → components/audio/components/GetSampledPCM16.ts} +14 -8
  40. package/src/components/audio/components/GetTTS.ts +61 -0
  41. package/src/{@modules/@eas/eas.getWavPCM16.ts → components/audio/components/GetWavPCM16.ts} +8 -8
  42. package/src/{@modules/@eas/eas.setAFSK.ts → components/audio/components/SetAFSK.ts} +12 -8
  43. package/src/{@modules/@eas/eas.setAsciiToBits.ts → components/audio/components/SetAsciiToBits.ts} +3 -4
  44. package/src/{@modules/@eas/eas.setAttentionTone.ts → components/audio/components/SetAttentionTone.ts} +11 -6
  45. package/src/{@modules/@eas/eas.setNoise.ts → components/audio/components/SetNoise.ts} +14 -8
  46. package/src/{@modules/@eas/eas.setRadioEffect.ts → components/audio/components/SetRadioEffect.ts} +14 -8
  47. package/src/{@modules/@eas/eas.setSameHeader.ts → components/audio/components/SetSameHeader.ts} +22 -15
  48. package/src/{@modules/@utilities/utilities.setWarning.ts → components/database/CreateQuery.ts} +18 -13
  49. package/src/{@modules/@database/database.cache.ts → components/database/GetCachedEvents.ts} +19 -14
  50. package/src/components/database/ImportBoundaries.ts +168 -0
  51. package/src/components/database/ImportBroadcastify.ts +60 -0
  52. package/src/components/database/ImportCities.ts +87 -0
  53. package/src/{@modules/@database/database.shapefiles.ts → components/database/ImportShapefiles.ts} +31 -37
  54. package/src/components/database/ImportStanza.ts +43 -0
  55. package/src/components/database/InitializeDatabase.ts +71 -0
  56. package/src/components/images/GenerateGraphic.ts +234 -0
  57. package/src/components/images/components/CreateRectangle.ts +22 -0
  58. package/src/components/images/components/CreateText.ts +22 -0
  59. package/src/components/images/modules/CreateSVG.ts +37 -0
  60. package/src/components/images/modules/GetGeographicalBoundaries.ts +72 -0
  61. package/src/components/images/modules/GetGeographicalCities.ts +58 -0
  62. package/src/components/images/modules/GetGeographicalEvents.ts +48 -0
  63. package/src/components/images/modules/GetGeometryBounds.ts +82 -0
  64. package/src/components/images/modules/GetParsedBoundary.ts +58 -0
  65. package/src/components/images/modules/GetSVGPath.ts +53 -0
  66. package/src/components/images/modules/NormalizePolygon.ts +57 -0
  67. package/src/{@modules/@stanza/stanza.getAwipsType.ts → components/stanza/GetAwipsType.ts} +19 -18
  68. package/src/components/stanza/ValidateStanza.ts +62 -0
  69. package/src/components/utilities/CreateHttp.ts +83 -0
  70. package/src/{@modules/@utilities/utilities.getFormattedTime.ts → components/utilities/GetFormattedTime.ts} +3 -3
  71. package/src/components/utilities/GetLatestIssuance.ts +32 -0
  72. package/src/components/utilities/GetMatched.ts +38 -0
  73. package/src/{@modules/@utilities/utilities.getShapeNearestPoint.ts → components/utilities/GetShapeNearestPoint.ts} +24 -19
  74. package/src/components/utilities/GetUnionPolygon.ts +36 -0
  75. package/src/components/utilities/QueueManager.ts +70 -0
  76. package/src/components/utilities/SetCronSchedule.ts +98 -0
  77. package/src/{@core/core.clearEvents.ts → components/utilities/SetDebug.ts} +17 -12
  78. package/src/components/utilities/SetEventEmit.ts +42 -0
  79. package/src/{@modules/@utilities/utilities.setSettings.ts → components/utilities/SetSettings.ts} +7 -7
  80. package/src/{@modules/@utilities/utilities.setSleep.ts → components/utilities/SetSleep.ts} +5 -5
  81. package/src/components/utilities/SetTimeoutAction.ts +64 -0
  82. package/src/components/utilities/SetWarning.ts +47 -0
  83. package/src/{@modules/@xmpp/xmpp.xDeploy.ts → components/xmpp/DeployXMPP.ts} +26 -27
  84. package/src/components/xmpp/ErrorXMPP.ts +27 -0
  85. package/src/{@modules/@xmpp/xmpp.xError.ts → components/xmpp/OfflineXMPP.ts} +17 -9
  86. package/src/{@modules/@xmpp/xmpp.xOnline.ts → components/xmpp/OnlineXMPP.ts} +21 -20
  87. package/src/components/xmpp/ReconnectXMPP.ts +64 -0
  88. package/src/components/xmpp/StanzaXMPP.ts +63 -0
  89. package/src/{@building/building.tags.ts → core/ClearEvents.ts} +12 -5
  90. package/src/{@core/core.getVersion.ts → core/GetEvents.ts} +5 -6
  91. package/src/core/GetNodes.ts +24 -0
  92. package/src/{@core/core.getNodes.ts → core/GetRandomEvent.ts} +5 -6
  93. package/src/core/GetVersion.ts +24 -0
  94. package/src/core/ManualEvent.ts +55 -0
  95. package/src/core/QueryStanza.ts +40 -0
  96. package/src/core/SetNode.ts +87 -0
  97. package/src/core/StartService.ts +62 -0
  98. package/src/core/StopService.ts +32 -0
  99. package/src/enums/AWIPS.ts +366 -0
  100. package/src/{@dictionaries/dictionaries.eventActions.ts → enums/Actions.ts} +3 -3
  101. package/src/{@dictionaries/dictionaries.eventCancelMessages.ts → enums/Cancellation.ts} +4 -3
  102. package/src/{@dictionaries/dictionaries.eventCauses.ts → enums/Causes.ts} +3 -3
  103. package/src/{@dictionaries/dictionaries.statusCorrelationText.ts → enums/Correlations.ts} +3 -3
  104. package/src/enums/Enhanced.ts +70 -0
  105. package/src/{@dictionaries/dictionaries.eventTypes.ts → enums/Events.ts} +3 -3
  106. package/src/{@dictionaries/dictionaries.regExp.ts → enums/Expressions.ts} +5 -4
  107. package/src/{@core/core.getEvents.ts → enums/GlobalFilter.ts} +7 -8
  108. package/src/{@dictionaries/dictionaries.hailStrings.ts → enums/Hail.ts} +9 -4
  109. package/src/{@dictionaries/dictionaries.officeICAOs.ts → enums/ICAO.ts} +5 -3
  110. package/src/enums/Matches.ts +60 -0
  111. package/src/{@dictionaries/dictionaries.eventProducts.ts → enums/Products.ts} +3 -3
  112. package/src/{@dictionaries/dictionaries.eventRecords.ts → enums/Records.ts} +3 -3
  113. package/src/{@dictionaries/dictionaries.eventSeverity.ts → enums/Severity.ts} +3 -3
  114. package/src/{@dictionaries/dictionaries.shapefileLinks.ts → enums/Shapefiles.ts} +3 -3
  115. package/src/enums/States.ts +136 -0
  116. package/src/{@dictionaries/dictionaries.eventStatus.ts → enums/Status.ts} +3 -3
  117. package/src/enums/Strings.ts +22 -0
  118. package/src/enums/Tags.ts +172 -0
  119. package/src/{@dictionaries/dictionaries.testSignatures.ts → enums/Testing.ts} +5 -4
  120. package/src/enums/Themes.ts +70 -0
  121. package/src/events/ParseAPI.ts +129 -0
  122. package/src/{@events/events.text.ts → events/ParseText.ts} +32 -23
  123. package/src/{@events/events.ugc.ts → events/ParseUGC.ts} +36 -26
  124. package/src/events/ParseVTEC.ts +88 -0
  125. package/src/index.ts +60 -38
  126. package/src/manager/MakeEvents.ts +110 -0
  127. package/src/manager/RemoveEvent.ts +74 -0
  128. package/src/{@manager/manager.setHash.ts → manager/SetHash.ts} +19 -15
  129. package/src/manager/UpdateEvents.ts +35 -0
  130. package/src/manager/UpdateNode.ts +62 -0
  131. package/src/manager/ValidateEvents.ts +144 -0
  132. package/src/{@parsers/@hvtec/hvtec.extract.ts → parsers/hvtec/HVExtract.ts} +13 -13
  133. package/src/parsers/text/GetDescriptionFromProduct.ts +59 -0
  134. package/src/parsers/text/GetEmbededText.ts +57 -0
  135. package/src/parsers/text/GetPolygonFromProduct.ts +71 -0
  136. package/src/parsers/text/GetStringText.ts +58 -0
  137. package/src/{@parsers/@text/text.getTextFromProduct.ts → parsers/text/GetTextFromProduct.ts} +12 -12
  138. package/src/parsers/ugc/GetCache.ts +24 -0
  139. package/src/{@parsers/@ugc/ugc.expiry.ts → parsers/ugc/GetExpiry.ts} +3 -3
  140. package/src/{@parsers/@ugc/ugc.header.ts → parsers/ugc/GetHeader.ts} +6 -6
  141. package/src/parsers/ugc/GetLocations.ts +57 -0
  142. package/src/parsers/ugc/GetZonePolygon.ts +73 -0
  143. package/src/{@parsers/@ugc/ugc.zones.ts → parsers/ugc/GetZones.ts} +3 -3
  144. package/src/{@modules/@utilities/utilities.setListener.ts → parsers/ugc/SetCache.ts} +13 -10
  145. package/src/{@parsers/@ugc/ugc.extract.ts → parsers/ugc/UGCExtract.ts} +15 -15
  146. package/src/{@parsers/@pvtec/pvtec.expires.ts → parsers/vtec/GetExpiry.ts} +3 -3
  147. package/src/parsers/vtec/VTECExtract.ts +49 -0
  148. package/src/{@types/types.attributes.ts → static/Attributes.ts} +2 -3
  149. package/src/{@types/type.event.ts → static/Event.ts} +11 -8
  150. package/src/{@types/type.properties.ts → static/Properties.ts} +17 -10
  151. package/src/{@types/types.stanza.ts → static/Stanza.ts} +2 -2
  152. package/src/tasks/CreateTasks.ts +123 -0
  153. package/src/{@core/core.stop.ts → tasks/TaskGenerateJSON.ts} +14 -12
  154. package/src/tasks/TaskGenerateText.ts +34 -0
  155. package/src/tasks/TaskSendNTFY.ts +97 -0
  156. package/src/tasks/TaskSendWebhook.ts +107 -0
  157. package/src/types/Actions.ts +37 -0
  158. package/src/{@types/types.hvtec.ts → types/HVTEC.ts} +6 -6
  159. package/src/{@types/types.hash.ts → types/Hash.ts} +5 -5
  160. package/src/types/Nodes.ts +29 -0
  161. package/src/{@types/types.settings.ts → types/Settings.ts} +42 -10
  162. package/src/{@types/types.compiled.ts → types/StanzaCompiled.ts} +15 -14
  163. package/src/{@types/types.ugc.ts → types/UGC.ts} +5 -5
  164. package/src/{@modules/@utilities/utilities.getSettings.ts → types/VTEC.ts} +12 -7
  165. package/tools/build.js +47 -0
  166. package/tsconfig.json +28 -1
  167. package/dist/index.d.mts +0 -239
  168. package/dist/index.d.ts +0 -239
  169. package/src/@building/building.create.ts +0 -42
  170. package/src/@building/building.properties.ts +0 -89
  171. package/src/@building/building.tracking.ts +0 -68
  172. package/src/@building/building.validate.ts +0 -147
  173. package/src/@core/core.getRandomEvent.ts +0 -25
  174. package/src/@core/core.setNode.ts +0 -81
  175. package/src/@core/core.start.ts +0 -61
  176. package/src/@dictionaries/dictionaries.betterEventNames.ts +0 -99
  177. package/src/@dictionaries/dictionaries.eventAwipAbreviations.ts +0 -364
  178. package/src/@dictionaries/dictionaries.eventTags.ts +0 -93
  179. package/src/@dictionaries/dictionaries.eventsMatchText.ts +0 -36
  180. package/src/@dictionaries/dictionaries.transcribedMessageReplacements.ts +0 -68
  181. package/src/@events/events.api.ts +0 -113
  182. package/src/@events/events.vtec.ts +0 -87
  183. package/src/@manager/manager.mkEvent.ts +0 -96
  184. package/src/@manager/manager.rmEvent.ts +0 -49
  185. package/src/@manager/manager.updateEvents.ts +0 -35
  186. package/src/@manager/manager.updateNodes.ts +0 -55
  187. package/src/@manager/manager.updateWebhooks.ts +0 -50
  188. package/src/@modules/@database/database.init.ts +0 -45
  189. package/src/@modules/@database/database.stanza.ts +0 -48
  190. package/src/@modules/@eas/eas.setEasTone.ts +0 -137
  191. package/src/@modules/@stanza/stanza.validate.ts +0 -50
  192. package/src/@modules/@utilities/utilities.createHttp.ts +0 -85
  193. package/src/@modules/@utilities/utilities.createWebhook.ts +0 -115
  194. package/src/@modules/@utilities/utilities.setCronSchedule.ts +0 -65
  195. package/src/@modules/@utilities/utilities.setEventEmit.ts +0 -41
  196. package/src/@modules/@utilities/utilities.setTimeoutAction.ts +0 -59
  197. package/src/@modules/@xmpp/xmpp.xOffline.ts +0 -38
  198. package/src/@modules/@xmpp/xmpp.xReconnect.ts +0 -64
  199. package/src/@modules/@xmpp/xmpp.xStanza.ts +0 -63
  200. package/src/@parsers/@pvtec/pvtec.extract.ts +0 -49
  201. package/src/@parsers/@text/text.getDescriptionFromProduct.ts +0 -53
  202. package/src/@parsers/@text/text.getPolygonFromProduct.ts +0 -32
  203. package/src/@parsers/@text/text.getXML.ts +0 -61
  204. package/src/@parsers/@ugc/ugc.coordinates.ts +0 -110
  205. package/src/@parsers/@ugc/ugc.locations.ts +0 -29
  206. package/src/@types/types.pvtec.ts +0 -30
  207. package/src/@types/types.webhook.ts +0 -27
  208. package/test.js +0 -91
  209. package/tsup.config.ts +0 -16
  210. /package/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
  211. /package/{LICENSE → .github/LICENSE} +0 -0
  212. /package/{SECURITY.md → .github/SECURITY.md} +0 -0
@@ -0,0 +1,110 @@
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: https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Independent Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { TypeEvent } from "StaticTypes/Event"
21
+ import { TypeSettings } from "Types/Settings"
22
+ import { EnumGlobalFilter } from "@Enums/GlobalFilter"
23
+ import { Bootstrap } from "@Bootstrap"
24
+ import { CreateTasks } from "@Tasks/CreateTasks"
25
+ import { SetHash } from "@Manager/SetHash"
26
+ import { UpdateNode } from "@Manager/UpdateNode"
27
+ import { SetEventEmit } from "@Utilities/SetEventEmit"
28
+ import { SetTimeoutAction } from "@Utilities/SetTimeoutAction"
29
+ import { GetStringText } from "@ParsingText/GetStringText"
30
+
31
+ export const MakeEvents = async (events: TypeEvent[]): Promise<void> => {
32
+ let tasked = [] as TypeEvent[];
33
+ const settings = Bootstrap.Settings as TypeSettings;
34
+ if (events.length === 0) return
35
+ await Promise.all(events.map(async event => {
36
+ const features = Bootstrap.Cache.Events.features;
37
+ const getHash = event.properties.metadata.hash;
38
+ const getTracking = event.properties.metadata.tracking;
39
+ const isEntry = Bootstrap.Cache.Hashes?.find(hash => hash.Tracking === getTracking)
40
+ const isHashed = isEntry?.Hashes?.includes(getHash) ?? false;
41
+ const isNodeFiltering = settings.GlobalSettings.EventFiltering.NodeLocationFiltering
42
+ const getNodes = Bootstrap.Cache.Nodes.features;
43
+ const getFeature = features.find(feature => feature.properties.metadata.tracking === getTracking);
44
+
45
+ if (isHashed || event.properties.status_metadata.is_expired) return
46
+ SetHash({ Event: event, Entry: isEntry })
47
+ await UpdateNode(event);
48
+ if (isNodeFiltering && getNodes.length > 0) {
49
+ if (!event.properties.metadata.filtered_proximity && !EnumGlobalFilter.includes(event.properties.event.toLowerCase())) {
50
+ return
51
+ }
52
+ }
53
+
54
+ const isRatelimited = SetTimeoutAction({ Identifier: getTracking, Interval: 1, Max: 1, AddTime: true })
55
+ const isLocal = event.properties.metadata.filtered_proximity ? `[LOCAL] ` : ``;
56
+ if (!isRatelimited.Limited) {
57
+ SetEventEmit({
58
+ Event: `onEventStatus`,
59
+ Metadata: {
60
+ Type: getFeature ? `Updated` : `New`,
61
+ Event: event
62
+ },
63
+ Tree: Bootstrap.Settings.EnhancedEventJournaling ? GetStringText(event).split('\n').filter(line => line.trim() !== '') : [],
64
+ Message: `${isLocal}[${getFeature ? 'Updated' : 'New'}] ${event.properties.event} (${event.properties.status}) (${event.properties.metadata.tracking})`
65
+ })
66
+ }
67
+
68
+ if (settings.GlobalSettings.EventManagement) {
69
+ if (event.properties.status_metadata.is_issued || event.properties.status_metadata.is_updated) {
70
+ if (getFeature) {
71
+ const getIndex = features.indexOf(getFeature);
72
+ const cHistory = getFeature?.properties?.metadata?.history ?? [];
73
+ const cLocations = getFeature?.properties?.locations?.split(";").map((l: string) => l.trim()) ?? [];
74
+ const cUgc = getFeature?.properties?.geocode?.ugc ?? [];
75
+
76
+ const iHistory = event.properties?.metadata?.history ?? [];
77
+ const iLocations = event.properties?.locations?.split(";").map((l: string) => l.trim()) ?? [];
78
+ const iUgc = event.properties?.geocode?.ugc ?? [];
79
+
80
+ 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);
81
+ const mLocations = [...cLocations, ...iLocations].filter((v, i, a) => a.indexOf(v) === i).join('; ');
82
+ const mUgc = [...cUgc, ...iUgc].filter((v, i, a) => a.indexOf(v) === i);
83
+
84
+ Bootstrap.Cache.Events.features[getIndex] = {
85
+ ...event,
86
+ properties: {
87
+ ...event.properties,
88
+ metadata: {
89
+ ...event?.properties?.metadata,
90
+ history: mHistory
91
+ },
92
+ locations: mLocations,
93
+ geocode: {
94
+ ...event?.properties?.geocode,
95
+ ugc: mUgc
96
+ },
97
+ }
98
+ };
99
+
100
+ tasked.push(Bootstrap.Cache.Events.features[getIndex])
101
+ } else {
102
+ features.push(event);
103
+ tasked.push(event)
104
+ }
105
+ }
106
+ }
107
+ }))
108
+ SetEventEmit({ Event: `onEventCache`, Metadata: Bootstrap.Cache.Events, Limited: true })
109
+ return await CreateTasks(tasked)
110
+ }
@@ -0,0 +1,74 @@
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: https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Independent Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { TypeEvent } from "StaticTypes/Event"
21
+ import { EnumStrings } from "@Enums/Strings"
22
+ import { Bootstrap } from "@Bootstrap"
23
+ import { SetEventEmit } from "@Utilities/SetEventEmit"
24
+ import { SetTimeoutAction } from "@Utilities/SetTimeoutAction"
25
+ import { CreateTasks } from "@Tasks/CreateTasks"
26
+
27
+ interface RemoveEventOptions {
28
+ Event: TypeEvent
29
+ IsTimeBasedExpiration: boolean
30
+ }
31
+
32
+ export const RemoveEvent = async ({ Event, IsTimeBasedExpiration }: RemoveEventOptions):Promise<void> => {
33
+ const gTracking = Event.properties.metadata.tracking;
34
+ const isTrackingEventLogged = Bootstrap.Cache.Events.features
35
+ .find(f => f?.properties?.metadata?.tracking === gTracking);
36
+ const isStatement = Event.properties.status_metadata.is_statement;
37
+
38
+ if (isTrackingEventLogged) {
39
+ Event.properties.expires = new Date().toISOString();
40
+ Event.properties.status = `Expired`;
41
+ Event.properties.status_metadata.is_expired = true;
42
+
43
+ const description = IsTimeBasedExpiration ? EnumStrings.cancellation
44
+ .replace(`{SENDER}`, Event.properties.geocode.office.name)
45
+ .replace(`{EVENT}`, Event.properties.event) : Event.properties.description
46
+ Event.properties.description = description;
47
+ Event.properties.metadata.raw = IsTimeBasedExpiration ? description : Event.properties.metadata.raw;
48
+ Event.properties.metadata.history.push({
49
+ description: IsTimeBasedExpiration ? description : Event.properties.description,
50
+ issued: Event.properties.expires,
51
+ status: Event.properties.status
52
+ })
53
+
54
+ Bootstrap.Cache.Events.features
55
+ .splice(Bootstrap.Cache.Events.features.indexOf(isTrackingEventLogged), 1);
56
+ Bootstrap.Cache.Hashes = Bootstrap.Cache.Hashes
57
+ .filter(hash => hash.Tracking !== gTracking);
58
+
59
+ if (!isStatement) {
60
+ SetEventEmit({
61
+ Event: `onEventStatus`,
62
+ Metadata: { type: `Removed`, event: Event },
63
+ Message: `[Removed] ${Event.properties.event} (${Event.properties.status}) (${gTracking})`
64
+ })
65
+ CreateTasks([Event])
66
+ }
67
+ SetTimeoutAction({ Identifier: gTracking, Expire: true })
68
+ }
69
+ SetEventEmit({
70
+ Event: `onEventCache`,
71
+ Metadata: Bootstrap.Cache.Events,
72
+ Limited: true
73
+ })
74
+ }
@@ -11,27 +11,31 @@
11
11
  Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
12
12
  Discord: https://atmosphericx-discord.scriptkitty.cafe
13
13
  Ko-Fi: https://ko-fi.com/k3yomi
14
- Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
14
+ Documentation: https://atmosphericx.scriptkitty.cafe/documentation
15
15
 
16
- Internal Package: @atmosx/event-product-parser
16
+ Independent Package: @atmosx/event-product-parser
17
17
 
18
18
  */
19
19
 
20
- import { TypeEvent } from "../@types/type.event";
21
- import { TypeHash } from "../@types/types.hash"
22
- import { bootstrap } from "../bootstrap"
20
+ import { TypeEvent } from "StaticTypes/Event"
21
+ import { TypeHash } from "Types/Hash"
22
+ import { Bootstrap } from "@Bootstrap"
23
23
 
24
+ interface SetHashOptions {
25
+ Event: TypeEvent
26
+ Entry: TypeHash
27
+ }
24
28
 
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 {
30
- bootstrap.cache.hashes.push({
31
- tracking: event.properties.metadata.tracking,
32
- hashes: [event.properties.metadata.hash],
33
- expires: event.properties.expires
29
+ export const SetHash = ({ Event, Entry }: SetHashOptions): void => {
30
+ if (!Entry) {
31
+ Bootstrap.Cache.Hashes.push({
32
+ Tracking: Event.properties.metadata.tracking,
33
+ Hashes: [Event.properties.metadata.hash],
34
+ Expires: Event.properties.expires
34
35
  });
36
+ return;
35
37
  }
36
-
38
+
39
+ Entry.Hashes.push(Event.properties.metadata.hash);
40
+ Entry.Expires = Event.properties.expires;
37
41
  }
@@ -0,0 +1,35 @@
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: https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Independent Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { TypeEvent } from "StaticTypes/Event"
21
+ import { Bootstrap } from "@Bootstrap"
22
+ import { SetEventEmit } from "@Utilities/SetEventEmit"
23
+ import { RemoveEvent } from "@Manager/RemoveEvent"
24
+
25
+ export const UpdateEvents = async (selected?: TypeEvent): Promise<void> => {
26
+ const events = Bootstrap.Cache.Events.features;
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: event, IsTimeBasedExpiration: true })
31
+ }
32
+ }
33
+ if (!selected) { await Promise.all(events.map(async (event) => { await update(event) })) }
34
+ if (selected) { await update(selected) }
35
+ }
@@ -0,0 +1,62 @@
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: https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Independent Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { TypeEvent } from "StaticTypes/Event"
21
+ import { Bootstrap } from "@Bootstrap"
22
+ import { GetEventNodes } from "@Building/GetEventNodes"
23
+ import { SetEventEmit } from "@Utilities/SetEventEmit"
24
+
25
+ export const UpdateNode = async (selected?: TypeEvent): Promise<void> => {
26
+ const events = Bootstrap.Cache.Events.features;
27
+ const ttl = Bootstrap.Settings.GlobalSettings.NodeTTL * 1e3;
28
+ let total = 0;
29
+
30
+ const TTLEvents = selected ? [selected] : events.filter((evt) => {
31
+ const lastUpdate = evt?.properties?.metadata?.updated ?? null;
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);
39
+ if (node.nodes.length > 0) {
40
+ total++;
41
+ }
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)));
51
+ }
52
+
53
+ if (total > 0) {
54
+ SetEventEmit({
55
+ Event: `onNodeUpdate`,
56
+ Metadata: {
57
+ Type: `global-update`,
58
+ Updated: total
59
+ },
60
+ })
61
+ }
62
+ }
@@ -0,0 +1,144 @@
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: https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Independent Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { TypeEvent } from "StaticTypes/Event"
21
+ import { TypeSettings } from "Types/Settings"
22
+ import { Bootstrap } from "@Bootstrap"
23
+ import { GetEventEnhancedName } from "@Building/GetEventEnhancedName"
24
+ import { GetEventSignature } from "@Building/GetEventSignature"
25
+ import { MakeEvents } from "@Manager/MakeEvents"
26
+ import { RemoveEvent } from "@Manager/RemoveEvent"
27
+ import { GetEventAttachments } from "@Building/GetEventAttachments"
28
+ import { SetEventEmit } from "@Utilities/SetEventEmit"
29
+ import { SetDebug } from "@Utilities/SetDebug"
30
+ import { GetMatched } from "@Utilities/GetMatched"
31
+ import { GetEventGeometry } from "@Building/GetEventGeometry"
32
+ import { createHash } from "crypto"
33
+
34
+ export const ValidateEvents = async (events: TypeEvent[]): Promise<void> => {
35
+ const tick = performance.now();
36
+ if (events.length === 0) return
37
+ const configurations = Bootstrap.Settings as TypeSettings
38
+ const sets = {} as Record<string, Set<string>>;
39
+ const bools = {} as Record<string, boolean>;
40
+ const megered = {...configurations.GlobalSettings, ...configurations.GlobalSettings.EventFiltering}
41
+ for (const key in megered) {
42
+ const setting = megered[key as keyof typeof megered]
43
+ if (Array.isArray(setting)) {
44
+ (sets as Record<string, any>)[key] = new Set(setting.map(item => String(item).toLowerCase()));
45
+ }
46
+ if (typeof setting === 'boolean') {
47
+ (bools as Record<string, any>)[key] = setting;
48
+ }
49
+ }
50
+ const isFiltered = (define: TypeEvent): boolean => {
51
+ const properties = define.properties;
52
+ const zones = properties.geocode.ugc;
53
+ const icao = properties.geocode.office.office
54
+
55
+ if (properties.status_metadata.is_test) {
56
+ SetEventEmit({ Event: `onTestProduct`, Metadata: define })
57
+ if (bools?.IgnoreTestProducts) return true;
58
+ }
59
+
60
+ if (properties.status_metadata.is_expired) {
61
+ SetEventEmit({ Event: `onExpiredProduct`, Metadata: define })
62
+ RemoveEvent({ Event: define, IsTimeBasedExpiration: false })
63
+ return true;
64
+ }
65
+
66
+ if (properties.metadata?.vtec?.Watch && properties.metadata.source != `events.api`) {
67
+ const isSPC = properties.metadata?.vtec?.PredictionCenter;
68
+ SetEventEmit({ Event: isSPC ? `onStormPredictionWatch` : `onNonStormPredictionWatch`, Metadata: define })
69
+ if (bools?.SPCWatchesOnly && !isSPC) {
70
+ return true;
71
+ }
72
+ if ((!bools?.SPCWatchesOnly) && isSPC) {
73
+ return true
74
+ }
75
+ }
76
+
77
+ for (const key in sets) {
78
+ const setting = sets[key]
79
+ const values = [...setting];
80
+ if (key === 'ListeningEvents' && setting.size > 0 && !GetMatched({ Strings: values, String: define.properties.event })) {
81
+ SetEventEmit({
82
+ Event: `onFilteredEvent`,
83
+ Metadata: define
84
+ });
85
+ return true
86
+ }
87
+ if (key === 'IgnoredEvents' && setting.size > 0 && GetMatched({ Strings: values, String: define.properties.event })) {
88
+ SetEventEmit({
89
+ Event: `onIgnoredEvent`,
90
+ Metadata: define
91
+ });
92
+ return true
93
+ }
94
+ if (key === 'ListeningICAO' && setting.size > 0 && icao != null && !setting.has(icao.toLowerCase())) {
95
+ SetEventEmit({
96
+ Event: `onFilteredICAO`,
97
+ Metadata: define
98
+ });
99
+ return true
100
+ }
101
+ if (key === 'IgnoredICAO' && setting.size > 0 && icao != null && setting.has(icao.toLowerCase())) {
102
+ SetEventEmit({
103
+ Event: `onIgnoredICAO`,
104
+ Metadata: define
105
+ });
106
+ return true
107
+ }
108
+ if (key === 'ListeningUGC' && setting.size > 0 && zones.length > 0 && !zones.some((ugc: string) => setting.has(ugc.toLowerCase()))) {
109
+ SetEventEmit({
110
+ Event: `onFilteredUGC`,
111
+ Metadata: define
112
+ });
113
+ return true
114
+ }
115
+ if (key === 'ListeningStates' && setting.size > 0 && zones.length > 0 && !zones.some((ugc: string) => setting.has(ugc.substring(0, 2).toLowerCase()))) {
116
+ SetEventEmit({
117
+ Event: `onFilteredState`,
118
+ Metadata: define
119
+ });
120
+ return true
121
+ }
122
+ }
123
+ return false
124
+ }
125
+
126
+ const filtering = events.filter((event: TypeEvent) => {
127
+ Bootstrap.Cache.Parsed = Bootstrap.Cache.Parsed.filter((e) => e !== event);
128
+ const define = GetEventSignature(event) as TypeEvent;
129
+ const pre = {...define,properties: {...define.properties, metadata: {...define.properties.metadata }}};
130
+ const properties = define.properties; delete pre.properties.metadata.ms; delete pre.properties.metadata.header;
131
+ const enhanced = properties.event = GetEventEnhancedName(event)
132
+ const filtered = isFiltered(define)
133
+ if (!filtered) {
134
+ event.geometry = !bools?.DisableGeometryParsing ? GetEventGeometry({ Event: event }) : null;
135
+ properties.metadata.attachments = GetEventAttachments(event)
136
+ }
137
+ properties.metadata.hash = createHash("sha256").update(JSON.stringify(pre)).digest("hex")
138
+ SetEventEmit({ Event: `onProductType${enhanced.replace(/\s+/g, '')}`, Metadata: define });
139
+ return !filtered
140
+ })
141
+
142
+ SetDebug({ Title: `ValidateEvents (${filtering.length}/${events.length})`, Message: `${Math.round(performance.now() - tick)}ms` })
143
+ await MakeEvents(filtering)
144
+ }
@@ -11,29 +11,29 @@
11
11
  Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
12
12
  Discord: https://atmosphericx-discord.scriptkitty.cafe
13
13
  Ko-Fi: https://ko-fi.com/k3yomi
14
- Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
14
+ Documentation: https://atmosphericx.scriptkitty.cafe/documentation
15
15
 
16
- Internal Package: @atmosx/event-product-parser
16
+ Independent Package: @atmosx/event-product-parser
17
17
 
18
18
  */
19
19
 
20
- import { TypeHVTEC } from "../../@types/types.hvtec";
21
- import { eventCauses } from "../../@dictionaries/dictionaries.eventCauses";
22
- import { eventRecords } from "../../@dictionaries/dictionaries.eventRecords";
23
- import { regExp } from "../../@dictionaries/dictionaries.regExp";
24
- import { eventSeverity } from "../../@dictionaries/dictionaries.eventSeverity";
20
+ import { TypeHVTEC } from "Types/HVTEC"
21
+ import { EnumCauses } from "@Enums/Causes"
22
+ import { EnumRecords } from "@Enums/Records"
23
+ import { EnumExpressions } from "@Enums/Expressions"
24
+ import { EnumSeverity } from "@Enums/Severity"
25
25
 
26
- export const hvExtract = (message: string): TypeHVTEC[] | null => {
27
- const getHVTECs = message.match(regExp.hvtec) ?? [];
26
+ export const HVExtract = (message: string): TypeHVTEC[] | null => {
27
+ const getHVTECs = message.match(EnumExpressions.hvtec) ?? [];
28
28
  const vtecs: TypeHVTEC[] = [];
29
29
  for (const vtec of getHVTECs) {
30
30
  const sub = vtec.split(`.`);
31
31
  if (sub.length < 7) continue;
32
32
  vtecs.push({
33
- hvtec: vtec,
34
- severity: eventSeverity[sub[1]],
35
- cause: eventCauses[sub[2]],
36
- record: eventRecords[sub[6]]
33
+ HVTEC: vtec,
34
+ Severity: EnumSeverity[sub[1]],
35
+ Cause: EnumCauses[sub[2]],
36
+ Record: EnumRecords[sub[6]]
37
37
  })
38
38
  }
39
39
  return vtecs.length > 0 ? vtecs : null;
@@ -0,0 +1,59 @@
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: https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Independent Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { EnumExpressions } from "@Enums/Expressions"
21
+
22
+ interface GetDescriptionFromProductOptions {
23
+ Message: string
24
+ Handle?: string
25
+ }
26
+
27
+ export const GetDescriptionFromProduct = ({ Message, Handle }: GetDescriptionFromProductOptions): string => {
28
+ const predefinedEndMarkers = ['&&', 'LAT...'];
29
+ const getEndIndex = (text: string, fromIndex = 0): number => {
30
+ const indices = predefinedEndMarkers
31
+ .map((marker) => text.indexOf(marker, fromIndex))
32
+ .filter((idx) => idx !== -1);
33
+ return indices.length ? Math.min(...indices) : -1;
34
+ };
35
+ const dates = Array.from(Message.matchAll(EnumExpressions.dateline));
36
+ if (dates.length) {
37
+ const lastMatch = dates[dates.length - 1][0];
38
+ const sIndx = Message.lastIndexOf(lastMatch);
39
+ if (sIndx !== -1) {
40
+ const endIndx = getEndIndex(Message, sIndx);
41
+ Message = Message.substring(sIndx + lastMatch.length, endIndx !== -1 ? endIndx : undefined).trimStart();
42
+ if (Message.startsWith('/')) Message = Message.slice(1).trimStart();
43
+ if (Handle && Message.includes(Handle)) {
44
+ const handleIdx = Message.indexOf(Handle);
45
+ Message = Message.substring(handleIdx + Handle.length).trimStart();
46
+ if (Message.startsWith('/')) Message = Message.slice(1).trimStart();
47
+ }
48
+ }
49
+ } else if (Handle) {
50
+ const handleIndx = Message.indexOf(Handle);
51
+ if (handleIndx !== -1) {
52
+ let afterHandle = Message.substring(handleIndx + Handle.length).trimStart();
53
+ if (afterHandle.startsWith('/')) afterHandle = afterHandle.slice(1).trimStart();
54
+ const latEnd = getEndIndex(afterHandle);
55
+ Message = latEnd !== -1 ? afterHandle.substring(0, latEnd).trim() : afterHandle.trim();
56
+ }
57
+ }
58
+ return Message.trim();
59
+ }
@@ -0,0 +1,57 @@
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: https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Independent Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { TypeEvent } from "StaticTypes/Event"
21
+
22
+ export const GetEmbededText = (event: TypeEvent): string => {
23
+ const line = (label: string, value: unknown, condition = true) => condition && value ? `${label} ${value}` : null;
24
+ const isStatement = event.properties.status_metadata.is_statement;
25
+ const isExpired = event.properties.status_metadata.is_expired;
26
+ return [
27
+ line(`**Locations:**`, event?.properties?.locations?.slice(0, 100)),
28
+ line(`**Issued:**`, `<t:${Math.floor(new Date(event.properties.issued).getTime() / 1000)}:R>`, !isExpired),
29
+ line(`**Expires:**`, `<t:${Math.floor(new Date(event.properties.expires).getTime() / 1000)}:R>`, !isStatement),
30
+ line(`**Damage Threat:**`, event?.properties?.parameters?.damage_threat, !isExpired),
31
+ line(`**Flood Threat:**`, event?.properties?.parameters?.flood_threat, !isExpired),
32
+ line(`**Tornado Threat:**`, event?.properties?.parameters?.tornado_threat, !isExpired),
33
+ line(`**Wind Gusts:**`, `${event?.properties?.parameters?.estimated_wind_gusts} ${event?.properties?.parameters?.wind_threat ? ` (${event?.properties?.parameters?.wind_threat})` : ''}`, !isExpired && event?.properties?.parameters?.estimated_wind_gusts != null),
34
+ line(`**Hail Size:**`, `${event?.properties?.parameters?.estimated_hail_size} ${event?.properties?.parameters?.hail_threat ? ` (${event?.properties?.parameters?.hail_threat})` : ''}`, !isExpired && event?.properties?.parameters?.estimated_hail_size != null),
35
+ line(`**Direction:**`, event?.properties?.parameters?.direction, !isExpired && event?.properties?.parameters?.direction != null),
36
+ line(`**Discussion:**`, event?.properties?.discussion_parameters?.discussion_number, !isExpired),
37
+ line(`**Concern:**`, event?.properties?.discussion_parameters?.discussion_concerning, !isExpired),
38
+ line(`**SPC Max Tornado Threat:**`, event?.properties?.discussion_parameters?.discussion_max_tornado, !isExpired),
39
+ line(`**SPC Max Hail Threat:**`, event?.properties?.discussion_parameters?.discussion_max_hail, !isExpired),
40
+ line(`**SPC Max Wind Threat:**`, event?.properties?.discussion_parameters?.discussion_max_wind, !isExpired),
41
+ line(`**SPC Watch Issuance Probability:**`, event?.properties?.discussion_parameters?.discussion_watch_issuance ? `${event?.properties?.discussion_parameters?.discussion_watch_issuance}%` : null, !isExpired),
42
+ line(`**Watch Number:**`, event?.properties?.watch_parameters?.watch_number, !isExpired),
43
+ line(`**Strong Tornadoes Probability:**`, event?.properties?.watch_parameters?.strong_tornadoes_probability ? `${event?.properties?.watch_parameters?.strong_tornadoes_probability}%` : null, !isExpired),
44
+ line(`**Additional Tornadoes Probability:**`, event?.properties?.watch_parameters?.additional_tornadoes_probability ? `${event?.properties?.watch_parameters?.additional_tornadoes_probability}%` : null, !isExpired),
45
+ line(`**Combined Hail/Wind Probability:**`, event?.properties?.watch_parameters?.combined_hail_wind_probability ? `${event?.properties?.watch_parameters?.combined_hail_wind_probability}%` : null, !isExpired),
46
+ line(`**Severe Hail Probability:**`, event?.properties?.watch_parameters?.severe_hail_probability ? `${event?.properties?.watch_parameters?.severe_hail_probability}%` : null, !isExpired),
47
+ line(`**Hail >2in Probability:**`, event?.properties?.watch_parameters?.hail_2in_probability ? `${event?.properties?.watch_parameters?.hail_2in_probability}%` : null, !isExpired),
48
+ line(`**Max Hail Inches:**`, event?.properties?.watch_parameters?.max_hail_in, !isExpired),
49
+ line(`**Severe Wind Probability:**`, event?.properties?.watch_parameters?.severe_wind_probability ? `${event?.properties?.watch_parameters?.severe_wind_probability}%` : null, !isExpired),
50
+ line(`**Max Surface Wind:**`, event?.properties?.watch_parameters?.max_wind_surface, !isExpired),
51
+ line(`**Max Tops (x100 feet):**`, event?.properties?.watch_parameters?.max_tops_x100feet, !isExpired),
52
+ line(`**Tags:**`, event?.properties?.parameters?.tags?.length > 0 ? event?.properties?.parameters?.tags.join(', ') : null, !isExpired),
53
+ line(`**Sender:**`, event?.properties?.geocode?.office?.name ? `${event?.properties?.geocode?.office?.name} (${event?.properties?.geocode?.office?.office})` : event?.properties?.geocode?.office?.office),
54
+ line(`**Tracking:**`, event?.properties?.metadata?.tracking),
55
+ line(`**Logs:**`, event?.properties?.metadata?.history?.length > 0 ? event?.properties?.metadata?.history.length : null),
56
+ ].filter(Boolean).join('\n');
57
+ }