@atmosx/event-product-parser 3.0.44 → 3.0.47
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.
- package/README.md +77 -31
- package/SUPPORTED_EVENTS.md +369 -0
- package/dist/cjs/index.cjs +681 -403
- package/dist/esm/index.mjs +659 -382
- package/dist/index.d.mts +54 -25
- package/dist/index.d.ts +54 -25
- package/package.json +1 -1
- package/src/@building/building.attachments.ts +83 -0
- package/src/@building/building.enhance.ts +7 -3
- package/src/@building/building.office.ts +2 -2
- package/src/@building/building.properties.ts +12 -11
- package/src/@building/building.signature.ts +11 -11
- package/src/@building/building.tags.ts +2 -2
- package/src/@building/building.tracking.ts +3 -3
- package/src/@building/building.validate.ts +2 -2
- package/src/@core/core.createEvent.ts +83 -0
- package/src/@dictionaries/{dictionaries.eventActions.ts → dictionaries.actions.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventAwipAbreviations.ts → dictionaries.awips.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventCancelMessages.ts → dictionaries.cancellation.ts} +2 -1
- package/src/@dictionaries/{dictionaries.eventCauses.ts → dictionaries.causes.ts} +1 -1
- package/src/@dictionaries/{dictionaries.statusCorrelationText.ts → dictionaries.correlations.ts} +1 -1
- package/src/@dictionaries/{dictionaries.betterEventNames.ts → dictionaries.enhanced.ts} +13 -2
- package/src/@dictionaries/{dictionaries.eventTypes.ts → dictionaries.events.ts} +1 -1
- package/src/@dictionaries/{dictionaries.regExp.ts → dictionaries.expressions.ts} +2 -1
- package/src/@dictionaries/{dictionaries.hailStrings.ts → dictionaries.hail.ts} +4 -1
- package/src/@dictionaries/{dictionaries.officeICAOs.ts → dictionaries.icao.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventsMatchText.ts → dictionaries.matches.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventProducts.ts → dictionaries.products.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventRecords.ts → dictionaries.records.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventSeverity.ts → dictionaries.severity.ts} +1 -1
- package/src/@dictionaries/{dictionaries.shapefileLinks.ts → dictionaries.shapefiles.ts} +1 -1
- package/src/@dictionaries/dictionaries.states.ts +72 -0
- package/src/@dictionaries/{dictionaries.eventStatus.ts → dictionaries.status.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventTags.ts → dictionaries.tags.ts} +1 -1
- package/src/@dictionaries/{dictionaries.testSignatures.ts → dictionaries.testing.ts} +3 -2
- package/src/@events/events.api.ts +18 -15
- package/src/@events/events.text.ts +2 -2
- package/src/@events/events.ugc.ts +2 -2
- package/src/@manager/manager.mkEvent.ts +3 -3
- package/src/@manager/manager.rmEvent.ts +3 -2
- package/src/@manager/manager.updateListener.ts +163 -0
- package/src/@modules/@database/database.broadcastify.ts +67 -0
- package/src/@modules/@database/database.init.ts +14 -1
- package/src/@modules/@database/database.shapefiles.ts +3 -6
- package/src/@modules/@eas/eas.getCleanDescription.ts +2 -0
- package/src/@modules/@eas/eas.getTTS.ts +19 -8
- package/src/@modules/@eas/eas.setEasTone.ts +15 -10
- package/src/@modules/@stanza/stanza.getAwipsType.ts +2 -2
- package/src/@modules/@stanza/stanza.validate.ts +3 -3
- package/src/@modules/@utilities/utilities.createHttp.ts +56 -47
- package/src/@modules/@utilities/utilities.setCronSchedule.ts +5 -2
- package/src/@modules/@xmpp/xmpp.xReconnect.ts +1 -1
- package/src/@parsers/@hvtec/hvtec.extract.ts +8 -8
- package/src/@parsers/@pvtec/pvtec.extract.ts +12 -12
- package/src/@parsers/@text/text.getDescriptionFromProduct.ts +2 -2
- package/src/@parsers/@text/text.getEmbed.ts +58 -0
- package/src/@parsers/@ugc/ugc.header.ts +3 -3
- package/src/@types/type.event.ts +3 -3
- package/src/@types/{type.webhook.ts → type.listener.ts} +11 -7
- package/src/@types/type.properties.ts +10 -9
- package/src/@types/type.settings.ts +13 -6
- package/src/bootstrap.ts +13 -6
- package/src/index.ts +2 -1
- package/test.js +30 -22
- package/src/@building/building.outlooks.ts +0 -36
- package/src/@manager/manager.createWebhook.ts +0 -129
- package/src/@manager/manager.updateWebhooks.ts +0 -49
- package/src/@modules/@utilities/utilities.isImageReady.ts +0 -27
|
@@ -18,15 +18,16 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { TypePVTEC } from "../../@types/type.pvtec";
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
21
|
+
import { dict_expressions } from "../../@dictionaries/dictionaries.expressions";
|
|
22
|
+
import { dict_products } from "../../@dictionaries/dictionaries.products";
|
|
23
|
+
import { dict_events } from "../../@dictionaries/dictionaries.events";
|
|
24
|
+
import { dict_actions } from "../../@dictionaries/dictionaries.actions";
|
|
25
|
+
import { dict_status } from "../../@dictionaries/dictionaries.status";
|
|
26
26
|
import { getExpiry } from "./pvtec.expires";
|
|
27
27
|
|
|
28
28
|
export const pvExtract = (message: string): TypePVTEC[] | null => {
|
|
29
|
-
|
|
29
|
+
if (!message) return null;
|
|
30
|
+
const getVTECs = message.match(dict_expressions.pvtec) ?? [];
|
|
30
31
|
const vtecs: TypePVTEC[] = [];
|
|
31
32
|
for (const vtec of getVTECs) {
|
|
32
33
|
const sub = vtec.split(`.`);
|
|
@@ -34,15 +35,14 @@ export const pvExtract = (message: string): TypePVTEC[] | null => {
|
|
|
34
35
|
const dates = sub[6]?.split(`-`);
|
|
35
36
|
vtecs.push({
|
|
36
37
|
vtec: vtec,
|
|
37
|
-
product:
|
|
38
|
+
product: dict_products[sub[0]],
|
|
38
39
|
tracking: `${sub[2]}.${sub[3]}.${sub[4]}.${sub[5]}`,
|
|
39
|
-
event: `${
|
|
40
|
-
status:
|
|
41
|
-
organization: message.match(
|
|
40
|
+
event: `${dict_events[sub[3]]} ${dict_actions[sub[4]]}`,
|
|
41
|
+
status: dict_status[sub[1]],
|
|
42
|
+
organization: message.match(dict_expressions.wmo)?.[0] ?? null,
|
|
42
43
|
expires: getExpiry(dates),
|
|
43
44
|
is_watch: (sub[4] == `A` || sub[4] == `Y`) && (sub[3] == `TO` || sub[3] == `SV`),
|
|
44
|
-
prediction_center: sub[2] == `KWNS`
|
|
45
|
-
? true : false
|
|
45
|
+
prediction_center: sub[2] == `KWNS` ? true : false
|
|
46
46
|
})
|
|
47
47
|
}
|
|
48
48
|
return vtecs.length > 0 ? vtecs : null;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { dict_expressions } from "../../@dictionaries/dictionaries.expressions";
|
|
21
21
|
|
|
22
22
|
interface GetDescriptionFromProductOptions {
|
|
23
23
|
message: string
|
|
@@ -26,7 +26,7 @@ interface GetDescriptionFromProductOptions {
|
|
|
26
26
|
|
|
27
27
|
export const getDescriptionFromProduct = (options: GetDescriptionFromProductOptions): string => {
|
|
28
28
|
let message = options.message;
|
|
29
|
-
const dates = Array.from(message.matchAll(
|
|
29
|
+
const dates = Array.from(message.matchAll(dict_expressions.dateline));
|
|
30
30
|
if (dates.length) {
|
|
31
31
|
const lastMatch = dates[dates.length - 1][0];
|
|
32
32
|
const sIndx = message.lastIndexOf(lastMatch);
|
|
@@ -0,0 +1,58 @@
|
|
|
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/type.event"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export const getEmebed = (event: TypeEvent): string => {
|
|
25
|
+
const line = (label: string, value: unknown, condition = true) => condition && value ? `${label} ${value}` : null;
|
|
26
|
+
const isStatement = event.properties.status_metadata.is_statement;
|
|
27
|
+
const isExpired = event.properties.status_metadata.is_expired;
|
|
28
|
+
return [
|
|
29
|
+
line(`**Locations:**`, event?.properties?.locations?.slice(0, 100)),
|
|
30
|
+
line(`**Issued:**`, `<t:${Math.floor(new Date(event.properties.issued).getTime() / 1000)}:R>`, !isExpired),
|
|
31
|
+
line(`**Expires:**`, `<t:${Math.floor(new Date(event.properties.expires).getTime() / 1000)}:R>`, !isStatement),
|
|
32
|
+
line(`**Damage Threat:**`, event?.properties?.parameters?.damage_threat, !isExpired),
|
|
33
|
+
line(`**Flood Threat:**`, event?.properties?.parameters?.flood_threat, !isExpired),
|
|
34
|
+
line(`**Tornado Threat:**`, event?.properties?.parameters?.tornado_threat, !isExpired),
|
|
35
|
+
line(`**Estimated 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),
|
|
36
|
+
line(`**Estimated 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),
|
|
37
|
+
line(`**Discussion:**`, event?.properties?.discussion_parameters?.discussion_number, !isExpired),
|
|
38
|
+
line(`**Concern:**`, event?.properties?.discussion_parameters?.discussion_concerning, !isExpired),
|
|
39
|
+
line(`**SPC Max Tornado Threat:**`, event?.properties?.discussion_parameters?.discussion_max_tornado, !isExpired),
|
|
40
|
+
line(`**SPC Max Hail Threat:**`, event?.properties?.discussion_parameters?.discussion_max_hail, !isExpired),
|
|
41
|
+
line(`**SPC Max Wind Threat:**`, event?.properties?.discussion_parameters?.discussion_max_wind, !isExpired),
|
|
42
|
+
line(`**SPC Watch Issuance Probability:**`, event?.properties?.discussion_parameters?.discussion_watch_issuance ? `${event?.properties?.discussion_parameters?.discussion_watch_issuance}%` : null, !isExpired),
|
|
43
|
+
line(`**Watch Number:**`, event?.properties?.watch_parameters?.watch_number, !isExpired),
|
|
44
|
+
line(`**Strong Tornadoes Probability:**`, event?.properties?.watch_parameters?.strong_tornadoes_probability ? `${event?.properties?.watch_parameters?.strong_tornadoes_probability}%` : null, !isExpired),
|
|
45
|
+
line(`**Additional Tornadoes Probability:**`, event?.properties?.watch_parameters?.additional_tornadoes_probability ? `${event?.properties?.watch_parameters?.additional_tornadoes_probability}%` : null, !isExpired),
|
|
46
|
+
line(`**Combined Hail/Wind Probability:**`, event?.properties?.watch_parameters?.combined_hail_wind_probability ? `${event?.properties?.watch_parameters?.combined_hail_wind_probability}%` : null, !isExpired),
|
|
47
|
+
line(`**Severe Hail Probability:**`, event?.properties?.watch_parameters?.severe_hail_probability ? `${event?.properties?.watch_parameters?.severe_hail_probability}%` : null, !isExpired),
|
|
48
|
+
line(`**Hail >2in Probability:**`, event?.properties?.watch_parameters?.hail_2in_probability ? `${event?.properties?.watch_parameters?.hail_2in_probability}%` : null, !isExpired),
|
|
49
|
+
line(`**Max Hail Inches:**`, event?.properties?.watch_parameters?.max_hail_in, !isExpired),
|
|
50
|
+
line(`**Severe Wind Probability:**`, event?.properties?.watch_parameters?.severe_wind_probability ? `${event?.properties?.watch_parameters?.severe_wind_probability}%` : null, !isExpired),
|
|
51
|
+
line(`**Max Surface Wind:**`, event?.properties?.watch_parameters?.max_wind_surface, !isExpired),
|
|
52
|
+
line(`**Max Tops (x100 feet):**`, event?.properties?.watch_parameters?.max_tops_x100feet, !isExpired),
|
|
53
|
+
line(`**Tags:**`, event?.properties?.parameters?.tags?.length > 0 ? event?.properties?.parameters?.tags.join(', ') : null, !isExpired),
|
|
54
|
+
line(`**Sender:**`, event?.properties?.geocode?.office?.name ? `${event?.properties?.geocode?.office?.name} (${event?.properties?.geocode?.office?.office})` : event?.properties?.geocode?.office?.office),
|
|
55
|
+
line(`**Tracking:**`, event?.properties?.metadata?.tracking),
|
|
56
|
+
line(`**Logs:**`, event?.properties?.metadata?.history?.length > 0 ? event?.properties?.metadata?.history.length : null),
|
|
57
|
+
].filter(Boolean).join('\n');
|
|
58
|
+
}
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { dict_expressions } from "../../@dictionaries/dictionaries.expressions"
|
|
21
21
|
|
|
22
22
|
export const getUGCHeader = (message: string): string | null => {
|
|
23
|
-
const start = message.search(
|
|
23
|
+
const start = message.search(dict_expressions.ugc1)
|
|
24
24
|
const sub = message.substring(start)
|
|
25
|
-
const end = sub.search(
|
|
25
|
+
const end = sub.search(dict_expressions.ugc2)
|
|
26
26
|
const fin = sub.substring(0, end)
|
|
27
27
|
.replace(/\s+/g, '')
|
|
28
28
|
.slice(0, -1);
|
package/src/@types/type.event.ts
CHANGED
|
@@ -46,8 +46,8 @@ export type TypeEvent = {
|
|
|
46
46
|
tracking: string
|
|
47
47
|
hash?: string
|
|
48
48
|
header: string
|
|
49
|
-
vtec
|
|
50
|
-
hvtec
|
|
49
|
+
vtec?: TypePVTEC | null
|
|
50
|
+
hvtec?: TypeHVTEC[] | null
|
|
51
51
|
nodes?: {
|
|
52
52
|
id?: string | number
|
|
53
53
|
coordinates: [number, number]
|
|
@@ -63,7 +63,7 @@ export type TypeEvent = {
|
|
|
63
63
|
issued: string
|
|
64
64
|
status: string
|
|
65
65
|
}[]
|
|
66
|
-
attachments?: string[]
|
|
66
|
+
attachments?: { name: string; link: string }[]
|
|
67
67
|
raw: string
|
|
68
68
|
}
|
|
69
69
|
} & BaseTypeEventProperties
|
|
@@ -17,12 +17,16 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
export type
|
|
21
|
-
webhook: string
|
|
22
|
-
title: string
|
|
23
|
-
message: string
|
|
24
|
-
upload: boolean
|
|
25
|
-
eas: boolean
|
|
26
|
-
rate: number
|
|
20
|
+
export type TypeListener = {
|
|
27
21
|
events: string[]
|
|
22
|
+
webhook?: {
|
|
23
|
+
enabled: boolean
|
|
24
|
+
destination: string
|
|
25
|
+
ratelimit: number
|
|
26
|
+
title: string
|
|
27
|
+
message: string
|
|
28
|
+
}
|
|
29
|
+
uploads?: {
|
|
30
|
+
file?: boolean, eas?: boolean, event?: boolean
|
|
31
|
+
}
|
|
28
32
|
}
|
|
@@ -21,8 +21,9 @@ import { TypeAttributes } from "./type.attributes";
|
|
|
21
21
|
|
|
22
22
|
export type TypeEventProperties = {
|
|
23
23
|
locations: string
|
|
24
|
+
locations_array: string[]
|
|
24
25
|
description: string
|
|
25
|
-
attributes
|
|
26
|
+
attributes?: TypeAttributes
|
|
26
27
|
geocode: {
|
|
27
28
|
office: {
|
|
28
29
|
name: string
|
|
@@ -33,7 +34,7 @@ export type TypeEventProperties = {
|
|
|
33
34
|
polygon: string
|
|
34
35
|
polygon_generated: boolean
|
|
35
36
|
}
|
|
36
|
-
parameters
|
|
37
|
+
parameters?: {
|
|
37
38
|
tags: string[]
|
|
38
39
|
instructions: string
|
|
39
40
|
source: string
|
|
@@ -66,12 +67,12 @@ export type TypeEventProperties = {
|
|
|
66
67
|
max_tops_x100feet: string
|
|
67
68
|
pds_watch: boolean
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
discussion_parameters?: {
|
|
71
|
+
discussion_number: string
|
|
72
|
+
discussion_concerning: string
|
|
73
|
+
discussion_max_tornado: string
|
|
74
|
+
discussion_max_hail: string
|
|
75
|
+
discussion_max_wind: string
|
|
76
|
+
discussion_watch_issuance: string
|
|
76
77
|
}
|
|
77
78
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { TypeListener } from "./type.listener"
|
|
21
21
|
|
|
22
22
|
export type TypeSettings = {
|
|
23
23
|
Database: string
|
|
@@ -48,6 +48,11 @@ export type TypeSettings = {
|
|
|
48
48
|
CallbackInterval: number
|
|
49
49
|
EventsEndpoint: string
|
|
50
50
|
}
|
|
51
|
+
BroadcastifySettings: {
|
|
52
|
+
BroadcastifyAttachments: boolean
|
|
53
|
+
BroadcastifyDatabase: string
|
|
54
|
+
BroadcastifyTags: string[]
|
|
55
|
+
},
|
|
51
56
|
GlobalSettings: {
|
|
52
57
|
EventManagement: boolean
|
|
53
58
|
BetterEventNames: boolean
|
|
@@ -57,7 +62,7 @@ export type TypeSettings = {
|
|
|
57
62
|
ShapefileSkipPoints: number
|
|
58
63
|
NodeTTL: number
|
|
59
64
|
NodeMinDistance: number
|
|
60
|
-
|
|
65
|
+
ListenerSettings?: TypeListener[]
|
|
61
66
|
EventFiltering: {
|
|
62
67
|
ListeningEvents: string[]
|
|
63
68
|
ListeningICAO: string[]
|
|
@@ -68,10 +73,12 @@ export type TypeSettings = {
|
|
|
68
73
|
NodeLocationFiltering: boolean
|
|
69
74
|
IgnoreTestProducts: boolean
|
|
70
75
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
ArchiveSettings: {
|
|
77
|
+
TTL: number
|
|
78
|
+
EventDirectory: string
|
|
79
|
+
TextDirectory: string
|
|
80
|
+
EasDirectory: string
|
|
81
|
+
EasToneout: string
|
|
75
82
|
}
|
|
76
83
|
}
|
|
77
84
|
}
|
package/src/bootstrap.ts
CHANGED
|
@@ -21,7 +21,7 @@ import path from 'path'
|
|
|
21
21
|
import { EventEmitter } from 'node:events';
|
|
22
22
|
|
|
23
23
|
export const bootstrap = {
|
|
24
|
-
version: `3.0.
|
|
24
|
+
version: `3.0.47`,
|
|
25
25
|
isReady: true,
|
|
26
26
|
ratelimits: {},
|
|
27
27
|
session_xmpp: null,
|
|
@@ -72,7 +72,12 @@ export const bootstrap = {
|
|
|
72
72
|
CallbackInterval: 15,
|
|
73
73
|
EventsEndpoint: `https://api.weather.gov/alerts/active`,
|
|
74
74
|
},
|
|
75
|
-
|
|
75
|
+
BroadcastifySettings: {
|
|
76
|
+
BroadcastifyAttachments: true,
|
|
77
|
+
BroadcastifyDatabase: `https://scriptkitty.cafe/ftp/@atmosphericx/assets/broadcastify.json`,
|
|
78
|
+
BroadcastifyTags: [`Ham`, `Air`, `Fire`, `Public Safety`, `Weather`, `EMS`, `Police`, `Rail`]
|
|
79
|
+
},
|
|
80
|
+
ListenerSettings: [],
|
|
76
81
|
GlobalSettings: {
|
|
77
82
|
EventManagement: true,
|
|
78
83
|
BetterEventNames: true,
|
|
@@ -92,10 +97,12 @@ export const bootstrap = {
|
|
|
92
97
|
NodeLocationFiltering: false,
|
|
93
98
|
IgnoreTestProducts: true,
|
|
94
99
|
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
ArchiveSettings: {
|
|
101
|
+
TTL: 60,
|
|
102
|
+
EventDirectory: null,
|
|
103
|
+
TextDirectory: null,
|
|
104
|
+
EasDirectory: null,
|
|
105
|
+
EasToneout: null
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
},
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { setEasTone } from './@modules/@eas/eas.setEasTone';
|
|
|
30
30
|
import { setNode } from "./@core/core.setNode"
|
|
31
31
|
import { getEvents } from "./@core/core.getEvents"
|
|
32
32
|
import { getNodes } from "./@core/core.getNodes"
|
|
33
|
+
import { createEvent } from "./@core/core.createEvent"
|
|
33
34
|
import { getRandomEvent } from './@core/core.getRandomEvent';
|
|
34
35
|
import { query } from './@core/core.query';
|
|
35
36
|
import { clearEvents } from './@core/core.clearEvents';
|
|
@@ -64,7 +65,7 @@ export class Manager {
|
|
|
64
65
|
export default Manager;
|
|
65
66
|
export type { TypeEvent } from './@types/type.event';
|
|
66
67
|
export {
|
|
67
|
-
setSettings, getEventGeometry,
|
|
68
|
+
setSettings, getEventGeometry, createEvent,
|
|
68
69
|
getCleanedEvent, stopService, clearEvents,
|
|
69
70
|
startService, setNode, getRandomEvent,
|
|
70
71
|
getEvents, getNodes, setEasTone, query
|
package/test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { Manager
|
|
1
|
+
const { Manager } = require(`../dist/cjs/index.cjs`)
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
Database: `
|
|
3
|
+
const Client = new Manager({
|
|
4
|
+
Database: `shapefiles.db`,
|
|
5
5
|
EnableWireService: false,
|
|
6
6
|
EnableJournal: true,
|
|
7
7
|
NOAAWeatherWireServiceSettings: {
|
|
@@ -29,29 +29,34 @@ const NOAAWeatherWireService = new Manager({
|
|
|
29
29
|
CallbackInterval: 30,
|
|
30
30
|
EventsEndpoint: `https://api.weather.gov/alerts/active`,
|
|
31
31
|
},
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
upload: true,
|
|
38
|
-
events: [`Severe Thunderstorm Warning`, `Radar Indicated Tornado Warning`],
|
|
39
|
-
rate: 1,
|
|
40
|
-
},
|
|
32
|
+
BroadcastifySettings: {
|
|
33
|
+
BroadcastifyAttachments: true,
|
|
34
|
+
BroadcastifyTags: [`Public Safety`, `Amateur Radio`, `Other`, `Rail`, `Aviation`, `Marine`, `Disaster Event`, `Special Event`]
|
|
35
|
+
},
|
|
36
|
+
ListenerSettings: [
|
|
41
37
|
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
events: [`*Thunderstorm Warning*`, `* Watch`],
|
|
39
|
+
webhook: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
destination: "https://discord.com/api/XXXXXXXXXX/XXXXXXXXXXX",
|
|
42
|
+
title: "AtmosphericX - (Thunderstorm Warnings and Watches)",
|
|
43
|
+
message: "<@&XXXXXXXXXXXXXXXXXXXXX>",
|
|
44
|
+
ratelimit: 1
|
|
45
|
+
},
|
|
46
|
+
uploads: {
|
|
47
|
+
eas: true,
|
|
48
|
+
file: true,
|
|
49
|
+
event: true
|
|
50
|
+
}
|
|
48
51
|
}
|
|
49
52
|
],
|
|
50
53
|
GlobalSettings: {
|
|
54
|
+
EventManagement: true,
|
|
51
55
|
BetterEventNames: true,
|
|
52
56
|
DisableGeometryParsing: false,
|
|
53
57
|
UseShapefileCoordinates: true,
|
|
54
58
|
SPCWatchesOnly: true,
|
|
59
|
+
ShapefileSkipPoints: 0,
|
|
55
60
|
NodeTTL: 60,
|
|
56
61
|
NodeMinDistance: 120,
|
|
57
62
|
EventFiltering: {
|
|
@@ -83,9 +88,12 @@ const NOAAWeatherWireService = new Manager({
|
|
|
83
88
|
NodeLocationFiltering: false,
|
|
84
89
|
IgnoreTestProducts: true,
|
|
85
90
|
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
ArchiveSettings: {
|
|
92
|
+
TTL: 600,
|
|
93
|
+
EventDirectory: `Storage/EventProducts`,
|
|
94
|
+
TextDirectory: `Storage/TextProducts`,
|
|
95
|
+
EasDirectory: `Storage/EasScenarios`,
|
|
96
|
+
EasToneout: `tone.wav`,
|
|
89
97
|
}
|
|
90
98
|
}
|
|
91
|
-
})
|
|
99
|
+
})
|
|
@@ -1,36 +0,0 @@
|
|
|
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/type.event"
|
|
21
|
-
import { getLatestIssuance } from "../@modules/@utilities/utilities.getLatestIssuance";
|
|
22
|
-
|
|
23
|
-
export const getEventAttachments = (event: TypeEvent): string[] | null => {
|
|
24
|
-
const latestTime = getLatestIssuance();
|
|
25
|
-
const validEvents = [
|
|
26
|
-
{ target: "Day 1", attachment: `https://www.spc.noaa.gov/products/outlook/day1otlk_${latestTime}.png` },
|
|
27
|
-
{ target: "Day 2", attachment: `https://www.spc.noaa.gov/products/outlook/day2otlk.png` },
|
|
28
|
-
{ target: "Day 3", attachment: `https://www.spc.noaa.gov/products/outlook/day3otlk.png` },
|
|
29
|
-
{ target: "Mesoscale Discussion", attachment: `https://www.spc.noaa.gov/products/md/mcd${event?.properties?.spc_parameters?.spc_number}.png` },
|
|
30
|
-
]
|
|
31
|
-
const isValid = validEvents.find(outlook => outlook.target == event.properties.event);
|
|
32
|
-
if (isValid) {
|
|
33
|
-
return isValid?.attachment ? [isValid.attachment] : [];
|
|
34
|
-
}
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
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/type.event";
|
|
21
|
-
import { setTimeoutAction } from "../@modules/@utilities/utilities.setTimeoutAction"
|
|
22
|
-
import { createHttp } from "../@modules/@utilities/utilities.createHttp"
|
|
23
|
-
import { isImageReady } from "../@modules/@utilities/utilities.isImageReady"
|
|
24
|
-
import { TypeWebhook } from "../@types/type.webhook";
|
|
25
|
-
import { getCleanedEvent } from "../@building/building.clean";
|
|
26
|
-
import { setEasTone } from "../@modules/@eas/eas.setEasTone";
|
|
27
|
-
import { readFileSync } from "fs";
|
|
28
|
-
import FormData from "form-data";
|
|
29
|
-
|
|
30
|
-
interface CreateWebhookOptions {
|
|
31
|
-
webhook: TypeWebhook
|
|
32
|
-
event: TypeEvent
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const createWebhook = async (options: CreateWebhookOptions): Promise<void> => {
|
|
36
|
-
const event = options.event.properties;
|
|
37
|
-
const settings = options.webhook;
|
|
38
|
-
const line = (label: string, value: unknown, condition = true) => condition && value ? `${label} ${value}` : null;
|
|
39
|
-
const isLimited = setTimeoutAction({ identifier: options.webhook.webhook, interval: options.webhook.rate, max: options.webhook.rate, addTime: true })
|
|
40
|
-
if (!isLimited.limited) {
|
|
41
|
-
const isStatement = event.status_metadata.is_statement;
|
|
42
|
-
const isExpired = event.status_metadata.is_expired;
|
|
43
|
-
let body = [
|
|
44
|
-
line(`**Locations:**`, event?.locations?.slice(0, 100)),
|
|
45
|
-
line(`**Issued:**`, `<t:${Math.floor(new Date(event.issued).getTime() / 1000)}:R>`),
|
|
46
|
-
line(`**Expires:**`, `<t:${Math.floor(new Date(event.expires).getTime() / 1000)}:R>`, !isExpired && !isStatement),
|
|
47
|
-
line(`**Damage Threat:**`, event.parameters.damage_threat, !isExpired),
|
|
48
|
-
line(`**Flood Threat:**`, event.parameters.flood_threat, !isExpired),
|
|
49
|
-
line(`**Tornado Threat:**`, event.parameters.tornado_threat, !isExpired),
|
|
50
|
-
line(`**Estimated Wind Gusts:**`, `${event.parameters.estimated_wind_gusts} ${event.parameters.wind_threat ? ` (${event.parameters.wind_threat})` : ''}`, !isExpired && event.parameters.estimated_wind_gusts != null),
|
|
51
|
-
line(`**Estimated Hail Size:**`, `${event.parameters.estimated_hail_size} ${event.parameters.hail_threat ? ` (${event.parameters.hail_threat})` : ''}`, !isExpired && event.parameters.estimated_hail_size != null),
|
|
52
|
-
line(`**Discussion:**`, event.spc_parameters.spc_number, !isExpired),
|
|
53
|
-
line(`**Concern:**`, event.spc_parameters.spc_concerning, !isExpired),
|
|
54
|
-
line(`**SPC Max Tornado Threat:**`, event.spc_parameters.spc_max_tornado, !isExpired),
|
|
55
|
-
line(`**SPC Max Hail Threat:**`, event.spc_parameters.spc_max_hail, !isExpired),
|
|
56
|
-
line(`**SPC Max Wind Threat:**`, event.spc_parameters.spc_max_wind, !isExpired),
|
|
57
|
-
line(`**SPC Watch Issuance Probability:**`, event.spc_parameters.spc_watch_issuance ? `${event.spc_parameters.spc_watch_issuance}%` : null, !isExpired),
|
|
58
|
-
line(`**Watch Number:**`, event.watch_parameters.watch_number, !isExpired),
|
|
59
|
-
line(`**Strong Tornadoes Probability:**`, event.watch_parameters.strong_tornadoes_probability ? `${event.watch_parameters.strong_tornadoes_probability}%` : null, !isExpired),
|
|
60
|
-
line(`**Additional Tornadoes Probability:**`, event.watch_parameters.additional_tornadoes_probability ? `${event.watch_parameters.additional_tornadoes_probability}%` : null, !isExpired),
|
|
61
|
-
line(`**Combined Hail/Wind Probability:**`, event.watch_parameters.combined_hail_wind_probability ? `${event.watch_parameters.combined_hail_wind_probability}%` : null, !isExpired),
|
|
62
|
-
line(`**Severe Hail Probability:**`, event.watch_parameters.severe_hail_probability ? `${event.watch_parameters.severe_hail_probability}%` : null, !isExpired),
|
|
63
|
-
line(`**Hail >2in Probability:**`, event.watch_parameters.hail_2in_probability ? `${event.watch_parameters.hail_2in_probability}%` : null, !isExpired),
|
|
64
|
-
line(`**Max Hail Inches:**`, event.watch_parameters.max_hail_in, !isExpired),
|
|
65
|
-
line(`**Severe Wind Probability:**`, event.watch_parameters.severe_wind_probability ? `${event.watch_parameters.severe_wind_probability}%` : null, !isExpired),
|
|
66
|
-
line(`**Max Surface Wind:**`, event.watch_parameters.max_wind_surface, !isExpired),
|
|
67
|
-
line(`**Max Tops (x100 feet):**`, event.watch_parameters.max_tops_x100feet, !isExpired),
|
|
68
|
-
line(`**Tags:**`, event.parameters.tags?.length > 0 ? event.parameters.tags.join(', ') : null, !isExpired),
|
|
69
|
-
line(`**Sender:**`, event.geocode?.office?.name ? `${event.geocode.office.name} (${event.geocode.office.office})` : event.geocode?.office?.office),
|
|
70
|
-
line(`**Tracking:**`, event.metadata?.tracking),
|
|
71
|
-
line(`**Logs:**`, event.metadata.history?.length > 0 ? event.metadata.history.length : null),
|
|
72
|
-
line(``, event.description ? '```' + '\n' + event.description.split('\n').map(l => l.trim()).filter(Boolean).join('\n') + '\n' + '```' : null, !isExpired)
|
|
73
|
-
].filter(Boolean).join('\n');
|
|
74
|
-
|
|
75
|
-
if (body.length > 1900) {
|
|
76
|
-
body = body.substring(0, 1900) + "\n\n[Message truncated due to length]";
|
|
77
|
-
const blocks = (body.match(/```/g) ?? []).length;
|
|
78
|
-
if (blocks % 2 !== 0) body += "```";
|
|
79
|
-
}
|
|
80
|
-
if (event.description.length < 25) { return }
|
|
81
|
-
|
|
82
|
-
const form = new FormData();
|
|
83
|
-
const img = event.metadata.attachments?.[0]
|
|
84
|
-
const embed = {
|
|
85
|
-
title: `${event.event} (${event.status})`,
|
|
86
|
-
description: body,
|
|
87
|
-
color: 16711680,
|
|
88
|
-
timestamp: new Date().toISOString(),
|
|
89
|
-
image: {},
|
|
90
|
-
footer: { text: settings.title }
|
|
91
|
-
};
|
|
92
|
-
if (img) {
|
|
93
|
-
for (let i = 0; i < 5; i++) {
|
|
94
|
-
const ok = await isImageReady(img)
|
|
95
|
-
if (ok) break
|
|
96
|
-
await new Promise(r => setTimeout(r, 5000))
|
|
97
|
-
}
|
|
98
|
-
const finalCheck = await isImageReady(img)
|
|
99
|
-
if (finalCheck) {
|
|
100
|
-
embed.image = { url: img }
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
form.append("payload_json", JSON.stringify({
|
|
104
|
-
username: settings.title ?? "AtmosphericX",
|
|
105
|
-
content: settings.message ?? "",
|
|
106
|
-
embeds: [embed]
|
|
107
|
-
}));
|
|
108
|
-
if (settings.upload) {
|
|
109
|
-
form.append("fUpload", Buffer.from(`${event.metadata.raw}\n\n${JSON.stringify((getCleanedEvent(event)), null, 2)}`), { filename: `${event.event}_${event.status}_${event.metadata.tracking}.txt`, contentType: "application/text" });
|
|
110
|
-
}
|
|
111
|
-
if (settings.eas) {
|
|
112
|
-
const audio = await setEasTone({
|
|
113
|
-
message: event.description,
|
|
114
|
-
header: event.metadata.header
|
|
115
|
-
});
|
|
116
|
-
const file = readFileSync(audio)
|
|
117
|
-
if (audio) {
|
|
118
|
-
form.append("fEas", Buffer.from(file), { filename: `${event.event}_${event.status}_${event.metadata.tracking}_eas.mp3`, contentType: "audio/mpeg" });
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
await createHttp({
|
|
122
|
-
url: settings.webhook,
|
|
123
|
-
timeout: 2000,
|
|
124
|
-
method: `POST`,
|
|
125
|
-
headers: form.getHeaders(),
|
|
126
|
-
body: form
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
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 { createWebhook } from "./manager.createWebhook";
|
|
21
|
-
import { TypeEvent } from "../@types/type.event";
|
|
22
|
-
import { TypeWebhook } from "../@types/type.webhook";
|
|
23
|
-
import { bootstrap } from "../bootstrap"
|
|
24
|
-
|
|
25
|
-
export const updateWebhooks = async (event: TypeEvent): Promise<void> => {
|
|
26
|
-
const settings = bootstrap.settings;
|
|
27
|
-
const webhooks = settings.WebhookSettings as TypeWebhook[];
|
|
28
|
-
const eventName = event.properties.event;
|
|
29
|
-
for (const socket of webhooks) {
|
|
30
|
-
const events = socket.events;
|
|
31
|
-
if (!events || events.length === 0) {
|
|
32
|
-
await createWebhook({ webhook: socket, event });
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
const matched = events.some(pattern => {
|
|
36
|
-
if (!pattern) return false;
|
|
37
|
-
if (pattern === "*" || pattern === eventName) return true;
|
|
38
|
-
if (pattern.includes("*")) {
|
|
39
|
-
const regex = "^" +
|
|
40
|
-
pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$";
|
|
41
|
-
return new RegExp(regex).test(eventName);
|
|
42
|
-
}
|
|
43
|
-
return false;
|
|
44
|
-
});
|
|
45
|
-
if (matched) {
|
|
46
|
-
await createWebhook({ webhook: socket, event });
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
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
|
-
export const isImageReady = async (url: string): Promise<boolean> => {
|
|
21
|
-
try {
|
|
22
|
-
const res = await fetch(url, { method: "HEAD" })
|
|
23
|
-
return res.ok
|
|
24
|
-
} catch {
|
|
25
|
-
return false
|
|
26
|
-
}
|
|
27
|
-
}
|