@atmosx/event-product-parser 3.0.44 → 3.0.46

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 (62) hide show
  1. package/README.md +66 -31
  2. package/SUPPORTED_EVENTS.md +369 -0
  3. package/dist/cjs/index.cjs +476 -373
  4. package/dist/esm/index.mjs +454 -352
  5. package/dist/index.d.mts +42 -24
  6. package/dist/index.d.ts +42 -24
  7. package/package.json +1 -1
  8. package/src/@building/{building.outlooks.ts → building.attachments.ts} +7 -1
  9. package/src/@building/building.enhance.ts +7 -3
  10. package/src/@building/building.office.ts +2 -2
  11. package/src/@building/building.properties.ts +11 -11
  12. package/src/@building/building.signature.ts +11 -11
  13. package/src/@building/building.tags.ts +2 -2
  14. package/src/@building/building.tracking.ts +3 -3
  15. package/src/@building/building.validate.ts +2 -2
  16. package/src/@core/core.createEvent.ts +82 -0
  17. package/src/@dictionaries/{dictionaries.eventActions.ts → dictionaries.actions.ts} +1 -1
  18. package/src/@dictionaries/{dictionaries.eventAwipAbreviations.ts → dictionaries.awips.ts} +1 -1
  19. package/src/@dictionaries/{dictionaries.eventCancelMessages.ts → dictionaries.cancellation.ts} +1 -1
  20. package/src/@dictionaries/{dictionaries.eventCauses.ts → dictionaries.causes.ts} +1 -1
  21. package/src/@dictionaries/{dictionaries.statusCorrelationText.ts → dictionaries.correlations.ts} +1 -1
  22. package/src/@dictionaries/{dictionaries.betterEventNames.ts → dictionaries.enhanced.ts} +13 -2
  23. package/src/@dictionaries/{dictionaries.eventTypes.ts → dictionaries.events.ts} +1 -1
  24. package/src/@dictionaries/{dictionaries.regExp.ts → dictionaries.expressions.ts} +1 -1
  25. package/src/@dictionaries/{dictionaries.hailStrings.ts → dictionaries.hail.ts} +4 -1
  26. package/src/@dictionaries/{dictionaries.officeICAOs.ts → dictionaries.icao.ts} +1 -1
  27. package/src/@dictionaries/{dictionaries.eventsMatchText.ts → dictionaries.matches.ts} +1 -1
  28. package/src/@dictionaries/{dictionaries.eventProducts.ts → dictionaries.products.ts} +1 -1
  29. package/src/@dictionaries/{dictionaries.eventRecords.ts → dictionaries.records.ts} +1 -1
  30. package/src/@dictionaries/{dictionaries.eventSeverity.ts → dictionaries.severity.ts} +1 -1
  31. package/src/@dictionaries/{dictionaries.shapefileLinks.ts → dictionaries.shapefiles.ts} +1 -1
  32. package/src/@dictionaries/{dictionaries.eventStatus.ts → dictionaries.status.ts} +1 -1
  33. package/src/@dictionaries/{dictionaries.eventTags.ts → dictionaries.tags.ts} +1 -1
  34. package/src/@dictionaries/{dictionaries.testSignatures.ts → dictionaries.test.ts} +3 -2
  35. package/src/@events/events.api.ts +17 -15
  36. package/src/@events/events.text.ts +2 -2
  37. package/src/@events/events.ugc.ts +2 -2
  38. package/src/@manager/manager.mkEvent.ts +3 -3
  39. package/src/@manager/manager.rmEvent.ts +3 -2
  40. package/src/@manager/manager.updateListener.ts +124 -0
  41. package/src/@modules/@database/database.shapefiles.ts +2 -2
  42. package/src/@modules/@eas/eas.getCleanDescription.ts +2 -0
  43. package/src/@modules/@eas/eas.getTTS.ts +19 -8
  44. package/src/@modules/@eas/eas.setEasTone.ts +15 -10
  45. package/src/@modules/@stanza/stanza.getAwipsType.ts +2 -2
  46. package/src/@modules/@stanza/stanza.validate.ts +3 -3
  47. package/src/@modules/@utilities/utilities.setCronSchedule.ts +4 -2
  48. package/src/@parsers/@hvtec/hvtec.extract.ts +8 -8
  49. package/src/@parsers/@pvtec/pvtec.extract.ts +12 -12
  50. package/src/@parsers/@text/text.getDescriptionFromProduct.ts +2 -2
  51. package/src/@parsers/@text/text.getEmbed.ts +60 -0
  52. package/src/@parsers/@ugc/ugc.header.ts +3 -3
  53. package/src/@types/type.event.ts +2 -2
  54. package/src/@types/{type.webhook.ts → type.listener.ts} +11 -7
  55. package/src/@types/type.properties.ts +9 -9
  56. package/src/@types/type.settings.ts +7 -6
  57. package/src/bootstrap.ts +7 -6
  58. package/src/index.ts +2 -1
  59. package/test.js +19 -19
  60. package/src/@manager/manager.createWebhook.ts +0 -129
  61. package/src/@manager/manager.updateWebhooks.ts +0 -49
  62. package/src/@modules/@utilities/utilities.isImageReady.ts +0 -27
@@ -18,22 +18,22 @@
18
18
  */
19
19
 
20
20
  import { TypeHVTEC } from "../../@types/type.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";
21
+ import { dict_causes } from "../../@dictionaries/dictionaries.causes";
22
+ import { dict_records } from "../../@dictionaries/dictionaries.records";
23
+ import { dict_expressions } from "../../@dictionaries/dictionaries.expressions";
24
+ import { dict_severity } from "../../@dictionaries/dictionaries.severity";
25
25
 
26
26
  export const hvExtract = (message: string): TypeHVTEC[] | null => {
27
- const getHVTECs = message.match(regExp.hvtec) ?? [];
27
+ const getHVTECs = message.match(dict_expressions.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
33
  hvtec: vtec,
34
- severity: eventSeverity[sub[1]],
35
- cause: eventCauses[sub[2]],
36
- record: eventRecords[sub[6]]
34
+ severity: dict_severity[sub[1]],
35
+ cause: dict_causes[sub[2]],
36
+ record: dict_records[sub[6]]
37
37
  })
38
38
  }
39
39
  return vtecs.length > 0 ? vtecs : null;
@@ -18,15 +18,16 @@
18
18
  */
19
19
 
20
20
  import { TypePVTEC } from "../../@types/type.pvtec";
21
- import { regExp } from "../../@dictionaries/dictionaries.regExp";
22
- import { eventProducts } from "../../@dictionaries/dictionaries.eventProducts";
23
- import { eventTypes } from "../../@dictionaries/dictionaries.eventTypes";
24
- import { eventActions } from "../../@dictionaries/dictionaries.eventActions";
25
- import { eventStatus } from "../../@dictionaries/dictionaries.eventStatus";
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
- const getVTECs = message.match(regExp.pvtec) ?? [];
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: eventProducts[sub[0]],
38
+ product: dict_products[sub[0]],
38
39
  tracking: `${sub[2]}.${sub[3]}.${sub[4]}.${sub[5]}`,
39
- event: `${eventTypes[sub[3]]} ${eventActions[sub[4]]}`,
40
- status: eventStatus[sub[1]],
41
- organization: message.match(regExp.wmo)?.[0] ?? null,
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 { regExp } from "../../@dictionaries/dictionaries.regExp";
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(regExp.dateline));
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,60 @@
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
+ line(``, event?.properties?.metadata?.attachments?.length > 0 ? `**Attachments:** ${event?.properties?.metadata?.attachments.map(attachment => `[Attachment #${event?.properties?.metadata?.attachments.indexOf(attachment) + 1}](${attachment})`).join(' | ')}` : null),
58
+ line(``, event?.properties?.description ? '```' + '\n' + event?.properties?.description.split('\n').map(l => l.trim()).filter(Boolean).join('\n') + '\n' + '```' : null, !isExpired),
59
+ ].filter(Boolean).join('\n');
60
+ }
@@ -17,12 +17,12 @@
17
17
 
18
18
  */
19
19
 
20
- import { regExp } from "../../@dictionaries/dictionaries.regExp"
20
+ import { dict_expressions } from "../../@dictionaries/dictionaries.expressions"
21
21
 
22
22
  export const getUGCHeader = (message: string): string | null => {
23
- const start = message.search(regExp.ugc1)
23
+ const start = message.search(dict_expressions.ugc1)
24
24
  const sub = message.substring(start)
25
- const end = sub.search(regExp.ugc2)
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);
@@ -46,8 +46,8 @@ export type TypeEvent = {
46
46
  tracking: string
47
47
  hash?: string
48
48
  header: string
49
- vtec: TypePVTEC | null
50
- hvtec: TypeHVTEC[]
49
+ vtec?: TypePVTEC | null
50
+ hvtec?: TypeHVTEC[] | null
51
51
  nodes?: {
52
52
  id?: string | number
53
53
  coordinates: [number, number]
@@ -17,12 +17,16 @@
17
17
 
18
18
  */
19
19
 
20
- export type TypeWebhook = {
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
31
+ }
28
32
  }
@@ -22,7 +22,7 @@ import { TypeAttributes } from "./type.attributes";
22
22
  export type TypeEventProperties = {
23
23
  locations: string
24
24
  description: string
25
- attributes: TypeAttributes
25
+ attributes?: TypeAttributes
26
26
  geocode: {
27
27
  office: {
28
28
  name: string
@@ -33,7 +33,7 @@ export type TypeEventProperties = {
33
33
  polygon: string
34
34
  polygon_generated: boolean
35
35
  }
36
- parameters: {
36
+ parameters?: {
37
37
  tags: string[]
38
38
  instructions: string
39
39
  source: string
@@ -66,12 +66,12 @@ export type TypeEventProperties = {
66
66
  max_tops_x100feet: string
67
67
  pds_watch: boolean
68
68
  }
69
- spc_parameters: {
70
- spc_number: string
71
- spc_concerning: string
72
- spc_max_tornado: string
73
- spc_max_hail: string
74
- spc_max_wind: string
75
- spc_watch_issuance: string
69
+ discussion_parameters?: {
70
+ discussion_number: string
71
+ discussion_concerning: string
72
+ discussion_max_tornado: string
73
+ discussion_max_hail: string
74
+ discussion_max_wind: string
75
+ discussion_watch_issuance: string
76
76
  }
77
77
  }
@@ -17,7 +17,7 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeWebhook } from "./type.webhook"
20
+ import { TypeListener } from "./type.listener"
21
21
 
22
22
  export type TypeSettings = {
23
23
  Database: string
@@ -57,7 +57,7 @@ export type TypeSettings = {
57
57
  ShapefileSkipPoints: number
58
58
  NodeTTL: number
59
59
  NodeMinDistance: number
60
- WebhookSettings?: TypeWebhook
60
+ ListenerSettings?: TypeListener[]
61
61
  EventFiltering: {
62
62
  ListeningEvents: string[]
63
63
  ListeningICAO: string[]
@@ -68,10 +68,11 @@ export type TypeSettings = {
68
68
  NodeLocationFiltering: boolean
69
69
  IgnoreTestProducts: boolean
70
70
  },
71
- EASSettings: {
72
- ArchiveTTL: number,
73
- ArchiveDirectory: string,
74
- IntroWavFile: string
71
+ ArchiveSettings: {
72
+ TTL: number,
73
+ TextDirectory: string
74
+ EasDirectory: string
75
+ EasToneout: string
75
76
  }
76
77
  }
77
78
  }
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.44`,
24
+ version: `3.0.46`,
25
25
  isReady: true,
26
26
  ratelimits: {},
27
27
  session_xmpp: null,
@@ -72,7 +72,7 @@ export const bootstrap = {
72
72
  CallbackInterval: 15,
73
73
  EventsEndpoint: `https://api.weather.gov/alerts/active`,
74
74
  },
75
- WebhookSettings: [],
75
+ ListenerSettings: [],
76
76
  GlobalSettings: {
77
77
  EventManagement: true,
78
78
  BetterEventNames: true,
@@ -92,10 +92,11 @@ export const bootstrap = {
92
92
  NodeLocationFiltering: false,
93
93
  IgnoreTestProducts: true,
94
94
  },
95
- EASSettings: {
96
- ArchiveTTL: 60,
97
- ArchiveDirectory: null,
98
- IntroWavFile: null,
95
+ ArchiveSettings: {
96
+ TTL: 60,
97
+ TextDirectory: null,
98
+ EasDirectory: null,
99
+ EasToneout: null
99
100
  }
100
101
  }
101
102
  },
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
1
  const { Manager, setSettings, getSettings, getListener } = require(`../dist/cjs/index.cjs`)
2
2
 
3
3
  const NOAAWeatherWireService = new Manager({
4
- Database: `shapefile-manager.db`,
4
+ Database: `shapefiles.db`,
5
5
  EnableWireService: false,
6
6
  EnableJournal: true,
7
7
  NOAAWeatherWireServiceSettings: {
@@ -29,22 +29,20 @@ const NOAAWeatherWireService = new Manager({
29
29
  CallbackInterval: 30,
30
30
  EventsEndpoint: `https://api.weather.gov/alerts/active`,
31
31
  },
32
- WebhookSettings: [
32
+ ListenerSettings: [
33
33
  {
34
- webhook: "https://discord.com/api/webhooks/XXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
35
- title: "AtmosphericX - (Severe Weather Events)",
36
- message: `<@user_id>`,
37
- upload: true,
38
- events: [`Severe Thunderstorm Warning`, `Radar Indicated Tornado Warning`],
39
- rate: 1,
40
- },
41
- {
42
- webhook: "https://discord.com/api/webhooks/XXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
43
- title: "AtmosphericX - (All Events)",
44
- message: `<@user_id>`,
45
- upload: false,
46
- events: [],
47
- rate: 5,
34
+ events: [`*Thunderstorm Warning*`, `* Watch`],
35
+ webhook: {
36
+ enabled: true,
37
+ destination: "https://discord.com/api/XXXXXXXXXX/XXXXXXXXXXX",
38
+ title: "AtmosphericX - (Thunderstorm Warnings and Watches)",
39
+ message: "<@&XXXXXXXXXXXXXXXXXXXXX>",
40
+ ratelimit: 1
41
+ },
42
+ uploads: {
43
+ eas: true,
44
+ file: true
45
+ }
48
46
  }
49
47
  ],
50
48
  GlobalSettings: {
@@ -83,9 +81,11 @@ const NOAAWeatherWireService = new Manager({
83
81
  NodeLocationFiltering: false,
84
82
  IgnoreTestProducts: true,
85
83
  },
86
- EASSettings: {
87
- ArchiveDirectory: null,
88
- IntroWavFile: null,
84
+ ArchiveSettings: {
85
+ TTL: 600,
86
+ TextDirectory: `Storage/TextProducts`,
87
+ EasDirectory: `Storage/EasScenarios`,
88
+ EasToneout: `tone.wav`,
89
89
  }
90
90
  }
91
91
  })
@@ -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
- }