@atmosx/event-product-parser 3.0.41 → 3.0.44

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 (61) hide show
  1. package/README.md +22 -0
  2. package/dist/cjs/index.cjs +1791 -1646
  3. package/dist/esm/index.mjs +1026 -882
  4. package/dist/index.d.mts +34 -3
  5. package/dist/index.d.ts +34 -3
  6. package/package.json +1 -1
  7. package/src/@building/building.create.ts +2 -2
  8. package/src/@building/building.enhance.ts +1 -1
  9. package/src/@building/building.geometry.ts +1 -1
  10. package/src/@building/building.headers.ts +1 -1
  11. package/src/@building/building.office.ts +3 -3
  12. package/src/@building/building.outlooks.ts +36 -0
  13. package/src/@building/building.properties.ts +11 -5
  14. package/src/@building/building.tracking.ts +7 -7
  15. package/src/@building/building.validate.ts +4 -2
  16. package/src/@core/core.query.ts +39 -0
  17. package/src/@core/core.setNode.ts +2 -2
  18. package/src/@core/core.start.ts +1 -1
  19. package/src/@events/events.api.ts +4 -1
  20. package/src/@events/events.text.ts +3 -2
  21. package/src/@events/events.ugc.ts +3 -2
  22. package/src/@events/events.vtec.ts +5 -4
  23. package/src/@manager/manager.createWebhook.ts +93 -81
  24. package/src/@manager/manager.mkEvent.ts +3 -2
  25. package/src/@manager/manager.setHash.ts +1 -1
  26. package/src/@manager/manager.updateWebhooks.ts +1 -2
  27. package/src/@modules/@database/database.cache.ts +1 -1
  28. package/src/@modules/@database/database.init.ts +1 -1
  29. package/src/@modules/@database/database.shapefiles.ts +1 -1
  30. package/src/@modules/@database/database.stanza.ts +2 -2
  31. package/src/@modules/@eas/eas.getCleanDescription.ts +89 -0
  32. package/src/@modules/@eas/eas.getTTS.ts +43 -0
  33. package/src/@modules/@eas/eas.setEasTone.ts +29 -24
  34. package/src/@modules/@stanza/stanza.getAwipsType.ts +1 -1
  35. package/src/@modules/@stanza/stanza.validate.ts +2 -2
  36. package/src/@modules/@utilities/utilities.getLatestIssuance.ts +32 -0
  37. package/src/@modules/@utilities/utilities.getSettings.ts +1 -1
  38. package/src/@modules/@utilities/utilities.isImageReady.ts +27 -0
  39. package/src/@modules/@utilities/utilities.setCronSchedule.ts +28 -2
  40. package/src/@modules/@utilities/utilities.setSettings.ts +1 -1
  41. package/src/@modules/@utilities/utilities.setWarning.ts +1 -1
  42. package/src/@modules/@xmpp/xmpp.xDeploy.ts +1 -1
  43. package/src/@modules/@xmpp/xmpp.xReconnect.ts +1 -1
  44. package/src/@modules/@xmpp/xmpp.xStanza.ts +1 -1
  45. package/src/@parsers/@hvtec/hvtec.extract.ts +1 -1
  46. package/src/@parsers/@pvtec/pvtec.extract.ts +1 -1
  47. package/src/@parsers/@ugc/ugc.extract.ts +1 -1
  48. package/src/@types/{types.compiled.ts → type.compiled.ts} +1 -1
  49. package/src/@types/type.event.ts +4 -2
  50. package/src/@types/type.properties.ts +3 -1
  51. package/src/@types/{types.settings.ts → type.settings.ts} +3 -2
  52. package/src/@types/{types.webhook.ts → type.webhook.ts} +1 -0
  53. package/src/bootstrap.ts +2 -1
  54. package/src/index.ts +3 -2
  55. package/src/@dictionaries/dictionaries.transcribedMessageReplacements.ts +0 -68
  56. /package/src/@types/{types.attributes.ts → type.attributes.ts} +0 -0
  57. /package/src/@types/{types.hash.ts → type.hash.ts} +0 -0
  58. /package/src/@types/{types.hvtec.ts → type.hvtec.ts} +0 -0
  59. /package/src/@types/{types.pvtec.ts → type.pvtec.ts} +0 -0
  60. /package/src/@types/{types.stanza.ts → type.stanza.ts} +0 -0
  61. /package/src/@types/{types.ugc.ts → type.ugc.ts} +0 -0
@@ -10491,7 +10491,7 @@ var require_form_data = __commonJS({
10491
10491
  import path from "path";
10492
10492
  import { EventEmitter } from "events";
10493
10493
  var bootstrap = {
10494
- version: `3.0.41`,
10494
+ version: `3.0.44`,
10495
10495
  isReady: true,
10496
10496
  ratelimits: {},
10497
10497
  session_xmpp: null,
@@ -10568,6 +10568,7 @@ var bootstrap = {
10568
10568
  IgnoreTestProducts: true
10569
10569
  },
10570
10570
  EASSettings: {
10571
+ ArchiveTTL: 60,
10571
10572
  ArchiveDirectory: null,
10572
10573
  IntroWavFile: null
10573
10574
  }
@@ -14304,7 +14305,7 @@ var officeICAOs = {
14304
14305
  // src/@building/building.office.ts
14305
14306
  var getEventOffice = (options) => {
14306
14307
  var _a, _b, _c, _d, _e, _f, _g;
14307
- const office = options.pVtec != null ? (_b = (_a = options.pVtec) == null ? void 0 : _a.tracking) == null ? void 0 : _b.split(`-`)[0] : (_e = (_c = options.attributes) == null ? void 0 : _c.cccc) != null ? _e : options.organization != null ? Array.isArray(options.organization) ? (_d = options.organization) == null ? void 0 : _d[0] : options.organization : null;
14308
+ const office = options.pVtec != null ? (_b = (_a = options.pVtec) == null ? void 0 : _a.tracking) == null ? void 0 : _b.split(`.`)[0] : (_e = (_c = options.attributes) == null ? void 0 : _c.cccc) != null ? _e : options.organization != null ? Array.isArray(options.organization) ? (_d = options.organization) == null ? void 0 : _d[0] : options.organization : null;
14308
14309
  const name = (_g = (_f = officeICAOs) == null ? void 0 : _f[office]) != null ? _g : null;
14309
14310
  return { office, name };
14310
14311
  };
@@ -14392,10 +14393,10 @@ var getEventTags = (message) => {
14392
14393
 
14393
14394
  // src/@building/building.properties.ts
14394
14395
  var properties = (options) => {
14395
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J;
14396
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L;
14396
14397
  const organization = (_b = (_a = options.message.match(regExp.wmo)) == null ? void 0 : _a[0]) != null ? _b : null;
14397
14398
  const polygons = getPolygonFromProduct(options.message);
14398
- return {
14399
+ const properties2 = {
14399
14400
  locations: (_e = (_d = (_c = options == null ? void 0 : options.ugc) == null ? void 0 : _c.locations) == null ? void 0 : _d.join(`; `)) != null ? _e : null,
14400
14401
  description: getDescriptionFromProduct({ message: options.message, handle: (_g = (_f = options == null ? void 0 : options.pVtec) == null ? void 0 : _f.vtec) != null ? _g : null }),
14401
14402
  attributes: options.attributes,
@@ -14421,26 +14422,32 @@ var properties = (options) => {
14421
14422
  hail_threat: (_t = getTextFromProduct({ message: options.message, find: [`HAIL THREAT...`], removal: [] })) != null ? _t : null
14422
14423
  },
14423
14424
  spc_parameters: {
14424
- spc_max_tornado: (_u = getTextFromProduct({ message: options.message, find: [`MOST PROBABLE PEAK TORNADO INTENSITY...`] })) != null ? _u : null,
14425
- spc_max_hail: (_v = getTextFromProduct({ message: options.message, find: [`MOST PROBABLE PEAK HAIL SIZE...`] })) != null ? _v : null,
14426
- spc_max_wind: (_w = getTextFromProduct({ message: options.message, find: [`MOST PROBABLE PEAK WIND GUST...`] })) != null ? _w : null,
14427
- spc_watch_issuance: (_x = getTextFromProduct({ message: options.message, find: [`Probability of Watch Issuance...`], removal: [`percent`] })) != null ? _x : null
14425
+ spc_number: (_u = getTextFromProduct({ message: options.message, find: [`Mesoscale Discussion `], removal: [`Mesoscale Discussion`, `Number`, `...`] })) != null ? _u : null,
14426
+ spc_concerning: (_v = getTextFromProduct({ message: options.message, find: [`Concerning...`] })) != null ? _v : null,
14427
+ spc_max_tornado: (_w = getTextFromProduct({ message: options.message, find: [`MOST PROBABLE PEAK TORNADO INTENSITY...`] })) != null ? _w : null,
14428
+ spc_max_hail: (_x = getTextFromProduct({ message: options.message, find: [`MOST PROBABLE PEAK HAIL SIZE...`] })) != null ? _x : null,
14429
+ spc_max_wind: (_y = getTextFromProduct({ message: options.message, find: [`MOST PROBABLE PEAK WIND GUST...`] })) != null ? _y : null,
14430
+ spc_watch_issuance: (_z = getTextFromProduct({ message: options.message, find: [`Probability of Watch Issuance...`], removal: [`percent`] })) != null ? _z : null
14428
14431
  },
14429
14432
  watch_parameters: {
14430
- watch_number: (_A = (_z = (_y = getTextFromProduct({ message: options.message, find: [`ITIES FOR`, `UPDATE FOR`, `Watch Number `], removal: [`%`, `<`, `:`] })) == null ? void 0 : _y.replace(/(WT|WS|)/g, "")) == null ? void 0 : _z.trim()) != null ? _A : null,
14433
+ watch_number: (_C = (_B = (_A = getTextFromProduct({ message: options.message, find: [`ITIES FOR`, `UPDATE FOR`, `Watch Number `], removal: [`%`, `<`, `:`] })) == null ? void 0 : _A.replace(/(WT|WS|)/g, "")) == null ? void 0 : _B.trim()) != null ? _C : null,
14431
14434
  watch_type: options.message.includes(`TORNADO WATCH`) ? `Tornado` : (options == null ? void 0 : options.message.includes(`SEVERE`)) ? `Severe` : null,
14432
- additional_tornadoes_probability: (_B = getTextFromProduct({ message: options.message, find: [`PROB OF 2 OR MORE TORNADOES`], removal: [`%`, `<`, `:`] })) != null ? _B : null,
14433
- strong_tornadoes_probability: (_C = getTextFromProduct({ message: options.message, find: [`PROB OF 1 OR MORE STRONG /EF2-EF5/ TORNADOES`], removal: [`%`, `<`, `:`] })) != null ? _C : null,
14434
- severe_wind_probability: (_D = getTextFromProduct({ message: options.message, find: [`PROB OF 10 OR MORE SEVERE WIND EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _D : null,
14435
- severe_hail_probability: (_E = getTextFromProduct({ message: options.message, find: [`PROB OF 10 OR MORE SEVERE HAIL EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _E : null,
14436
- hail_2in_probability: (_F = getTextFromProduct({ message: options.message, find: [`PROB OF 1 OR MORE HAIL EVENTS >= 2 INCHES`], removal: [`%`, `<`, `:`] })) != null ? _F : null,
14437
- combined_hail_wind_probability: (_G = getTextFromProduct({ message: options.message, find: [`PROB OF 6 OR MORE COMBINED SEVERE HAIL/WIND EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _G : null,
14438
- max_hail_in: (_H = getTextFromProduct({ message: options.message, find: [`MAX HAIL /INCHES/`], removal: [`%`, `<`, `:`] })) != null ? _H : null,
14439
- max_wind_surface: (_I = getTextFromProduct({ message: options.message, find: [`MAX WIND GUSTS SURFACE /KNOTS/`], removal: [`%`, `<`, `:`] })) != null ? _I : null,
14440
- max_tops_x100feet: (_J = getTextFromProduct({ message: options.message, find: [`MAX TOPS /X 100 FEET/`], removal: [`%`, `<`, `:`] })) != null ? _J : null,
14435
+ additional_tornadoes_probability: (_D = getTextFromProduct({ message: options.message, find: [`PROB OF 2 OR MORE TORNADOES`], removal: [`%`, `<`, `:`] })) != null ? _D : null,
14436
+ strong_tornadoes_probability: (_E = getTextFromProduct({ message: options.message, find: [`PROB OF 1 OR MORE STRONG /EF2-EF5/ TORNADOES`], removal: [`%`, `<`, `:`] })) != null ? _E : null,
14437
+ severe_wind_probability: (_F = getTextFromProduct({ message: options.message, find: [`PROB OF 10 OR MORE SEVERE WIND EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _F : null,
14438
+ severe_hail_probability: (_G = getTextFromProduct({ message: options.message, find: [`PROB OF 10 OR MORE SEVERE HAIL EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _G : null,
14439
+ hail_2in_probability: (_H = getTextFromProduct({ message: options.message, find: [`PROB OF 1 OR MORE HAIL EVENTS >= 2 INCHES`], removal: [`%`, `<`, `:`] })) != null ? _H : null,
14440
+ combined_hail_wind_probability: (_I = getTextFromProduct({ message: options.message, find: [`PROB OF 6 OR MORE COMBINED SEVERE HAIL/WIND EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _I : null,
14441
+ max_hail_in: (_J = getTextFromProduct({ message: options.message, find: [`MAX HAIL /INCHES/`], removal: [`%`, `<`, `:`] })) != null ? _J : null,
14442
+ max_wind_surface: (_K = getTextFromProduct({ message: options.message, find: [`MAX WIND GUSTS SURFACE /KNOTS/`], removal: [`%`, `<`, `:`] })) != null ? _K : null,
14443
+ max_tops_x100feet: (_L = getTextFromProduct({ message: options.message, find: [`MAX TOPS /X 100 FEET/`], removal: [`%`, `<`, `:`] })) != null ? _L : null,
14441
14444
  pds_watch: getTextFromProduct({ message: options.message, find: [`PARTICULARLY DANGEROUS SITUATION`], removal: [`%`, `<`, `:`] }) === `YES`
14442
14445
  }
14443
14446
  };
14447
+ if (isNaN(Number(properties2.watch_parameters.watch_number))) {
14448
+ properties2.watch_parameters.watch_number = null;
14449
+ }
14450
+ return properties2;
14444
14451
  };
14445
14452
 
14446
14453
  // src/@building/building.headers.ts
@@ -14474,16 +14481,16 @@ var eventsMatchText = {
14474
14481
  // src/@building/building.tracking.ts
14475
14482
  var getEventTracking = (options) => {
14476
14483
  var _a, _b, _c, _d, _e, _f, _g;
14477
- const proprties = options.properties;
14484
+ const properties2 = options.properties;
14478
14485
  const attributes = options.attributes;
14479
14486
  const stanza = options.stanza;
14480
14487
  const vtec2 = options.vtec;
14481
14488
  if (options.type === `RAW`) {
14482
- const getWatchNumber = (_a = proprties.watch_parameters.watch_number) != null ? _a : null;
14489
+ const getWatchNumber = (_a = properties2.watch_parameters.watch_number) != null ? _a : null;
14483
14490
  if (getWatchNumber) {
14484
- return `${proprties.geocode.office.office}.${stanza.getType.prefix}.A.${getWatchNumber}`;
14491
+ return `${properties2.geocode.office.office}.${stanza.getType.prefix}.A.${getWatchNumber}`;
14485
14492
  }
14486
- return `${proprties.geocode.office.office}.${attributes.ttaaii}.${(_b = attributes.id.slice(-4).replace(`.`, ``)) != null ? _b : "0"}`;
14493
+ return `${properties2.geocode.office.office}.${attributes.ttaaii}.${(_b = attributes.id.slice(-4).replace(`.`, ``)) != null ? _b : "0"}`;
14487
14494
  }
14488
14495
  if (options.type === `VTEC`) {
14489
14496
  return vtec2.tracking;
@@ -14781,276 +14788,727 @@ var createHttp = (options) => __async(null, null, function* () {
14781
14788
  });
14782
14789
  });
14783
14790
 
14784
- // src/@manager/manager.createWebhook.ts
14785
- var import_form_data = __toESM(require_form_data());
14786
- var createWebhook = (options) => __async(null, null, function* () {
14787
- var _a, _b, _c, _d, _e, _f;
14788
- const event = options.event.properties;
14789
- const settings = options.webhook;
14790
- let body = [
14791
- event.locations ? `**Locations**: ${event.locations.slice(0, 100)}` : null,
14792
- event.issued ? `**Issued**: <t:${Math.floor(new Date(event.issued).getTime() / 1e3)}:R>` : null,
14793
- event.expires && event.status != `Statement` ? `**Expires**: <t:${Math.floor(new Date(event.expires).getTime() / 1e3)}:R>` : null,
14794
- (() => {
14795
- var _a2, _b2;
14796
- const val = (_a2 = event.parameters.estimated_wind_gusts) != null ? _a2 : null;
14797
- const th = (_b2 = event.parameters.wind_threat) != null ? _b2 : null;
14798
- const combined = [val, th].filter(Boolean).join(" ");
14799
- return combined ? `**Wind Gusts**: ${val} ${th ? `(${th})` : ""}` : null;
14800
- })(),
14801
- (() => {
14802
- var _a2, _b2;
14803
- const val = (_a2 = event.parameters.estimated_hail_size) != null ? _a2 : null;
14804
- const th = (_b2 = event.parameters.hail_threat) != null ? _b2 : null;
14805
- return (val != null ? val : th) ? `**Hail Threat**: ${val} ${th ? `(${th})` : ""}` : null;
14806
- })(),
14807
- event.parameters.damage_threat ? `**Damage Threat**: ${event.parameters.damage_threat}` : null,
14808
- event.parameters.flood_threat ? `**Flood Threat**: ${event.parameters.flood_threat}` : null,
14809
- event.parameters.tornado_threat ? `**Tornado Threat**: ${event.parameters.tornado_threat}` : null,
14810
- event.spc_parameters.spc_max_tornado ? `**Max Tornado Threat**: ${event.spc_parameters.spc_max_tornado}` : null,
14811
- event.spc_parameters.spc_max_hail ? `**Max Hail Threat**: ${event.spc_parameters.spc_max_hail}` : null,
14812
- event.spc_parameters.spc_max_wind ? `**Max Wind Threat**: ${event.spc_parameters.spc_max_wind}` : null,
14813
- event.spc_parameters.spc_watch_issuance ? `**Watch Issuance**: ${event.spc_parameters.spc_watch_issuance}%` : null,
14814
- event.watch_parameters.watch_number ? `**Watch Number**: ${event.watch_parameters.watch_number}` : null,
14815
- event.watch_parameters.strong_tornadoes_probability ? `**Strong Tornadoes Probability**: ${event.watch_parameters.strong_tornadoes_probability}%` : null,
14816
- event.watch_parameters.additional_tornadoes_probability ? `**Additional Tornadoes Probability**: ${event.watch_parameters.additional_tornadoes_probability}%` : null,
14817
- event.watch_parameters.combined_hail_wind_probability ? `**Combined Hail/Wind Probability**: ${event.watch_parameters.combined_hail_wind_probability}%` : null,
14818
- event.watch_parameters.severe_hail_probability ? `**Severe Hail Probability**: ${event.watch_parameters.severe_hail_probability}%` : null,
14819
- event.watch_parameters.hail_2in_probability ? `**Hail \u22652in Probability**: ${event.watch_parameters.hail_2in_probability}%` : null,
14820
- event.watch_parameters.max_hail_in ? `**Max Hail Inches**: ${event.watch_parameters.max_hail_in}` : null,
14821
- event.watch_parameters.severe_wind_probability ? `**Severe Wind Probability**: ${event.watch_parameters.severe_wind_probability}%` : null,
14822
- event.watch_parameters.max_wind_surface ? `**Max Surface Wind**: ${event.watch_parameters.max_wind_surface}` : null,
14823
- event.watch_parameters.max_tops_x100feet ? `**Max Tops (x100 feet)**: ${event.watch_parameters.max_tops_x100feet}` : null,
14824
- ((_a = event.parameters.tags) == null ? void 0 : _a.length) > 0 ? `**Tags**: ${event.parameters.tags.join(", ")}` : null,
14825
- (() => {
14826
- var _a2, _b2, _c2, _d2, _e2, _f2;
14827
- const val = (_c2 = (_b2 = (_a2 = event.geocode) == null ? void 0 : _a2.office) == null ? void 0 : _b2.name) != null ? _c2 : `N/A`;
14828
- const th = (_f2 = (_e2 = (_d2 = event.geocode) == null ? void 0 : _d2.office) == null ? void 0 : _e2.office) != null ? _f2 : null;
14829
- return (val != null ? val : th) ? `**Sender**: ${val} ${th ? `(${th})` : ""}` : null;
14830
- })(),
14831
- ((_b = event.metadata) == null ? void 0 : _b.tracking) ? `**Tracking**: ${event.metadata.tracking}` : null,
14832
- ((_c = event.metadata.history) == null ? void 0 : _c.length) > 0 ? `**Updates**: ${event.metadata.history.length}` : null,
14833
- (() => {
14834
- var _a2;
14835
- if (event.status == `expires`) {
14836
- return null;
14837
- }
14838
- const desc = ((_a2 = event.description) != null ? _a2 : "").split("\n").map((l) => l.trim()).filter(Boolean).join("\n");
14839
- return desc ? "```\n" + desc + "\n```" : null;
14840
- })()
14841
- ].filter(Boolean).join("\n");
14842
- const isLimited = setTimeoutAction({ identifier: options.webhook.webhook, interval: options.webhook.rate, max: options.webhook.rate, addTime: true });
14843
- if (isLimited.limited) {
14844
- return;
14845
- }
14846
- if (body.length > 1900) {
14847
- body = body.substring(0, 1900) + "\n\n[Message truncated due to length]";
14848
- const blocks = ((_d = body.match(/```/g)) != null ? _d : []).length;
14849
- if (blocks % 2 !== 0) body += "```";
14850
- }
14851
- const form = new import_form_data.default();
14852
- const embed = {
14853
- title: `${event.event} (${event.status})`,
14854
- description: body,
14855
- color: 16711680,
14856
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
14857
- footer: { text: settings.title }
14858
- };
14859
- form.append("payload_json", JSON.stringify({
14860
- username: (_e = settings.title) != null ? _e : "AtmosphericX",
14861
- content: (_f = settings.message) != null ? _f : "",
14862
- embeds: [embed]
14863
- }));
14864
- if (settings.upload) {
14865
- form.append("file", Buffer.from(JSON.stringify(getCleanedEvent(event), null, 2)), {
14866
- filename: `${event.event}_${event.status}_${event.metadata.tracking}.json`,
14867
- contentType: "application/json"
14868
- });
14869
- }
14870
- yield createHttp({
14871
- url: settings.webhook,
14872
- timeout: 2e3,
14873
- method: `POST`,
14874
- headers: form.getHeaders(),
14875
- body: form
14876
- });
14877
- });
14878
-
14879
- // src/@manager/manager.updateWebhooks.ts
14880
- var updateWebhooks = (event) => __async(null, null, function* () {
14881
- const settings = bootstrap.settings;
14882
- const webhooks = settings.WebhookSettings;
14883
- const eventName = event.properties.event;
14884
- for (const socket of webhooks) {
14885
- const events = socket.events;
14886
- if (!events || events.length === 0) {
14887
- yield createWebhook({ webhook: socket, event });
14888
- continue;
14889
- }
14890
- const matched = events.some((pattern) => {
14891
- if (!pattern) return false;
14892
- if (pattern === "*" || pattern === eventName) return true;
14893
- if (pattern.includes("*")) {
14894
- const regex = "^" + pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$";
14895
- return new RegExp(regex).test(eventName);
14896
- }
14897
- return false;
14898
- });
14899
- if (matched) {
14900
- yield createWebhook({ webhook: socket, event });
14901
- }
14791
+ // src/@modules/@utilities/utilities.isImageReady.ts
14792
+ var isImageReady = (url) => __async(null, null, function* () {
14793
+ try {
14794
+ const res = yield fetch(url, { method: "HEAD" });
14795
+ return res.ok;
14796
+ } catch (e) {
14797
+ return false;
14902
14798
  }
14903
14799
  });
14904
14800
 
14905
- // src/@modules/@utilities/utilities.getShapeNearestPoint.ts
14906
- var getShapeNearestPoint = (coordinates, point) => {
14907
- if (!coordinates || !point) {
14908
- return { proximity: false, point: [0, 0], distance: null };
14801
+ // src/@modules/@eas/eas.getWavPCM16.ts
14802
+ var getWavPCM16 = (buffer) => {
14803
+ if (buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WAVE") {
14804
+ return null;
14909
14805
  }
14910
- const normalize = (coords) => {
14911
- if (!Array.isArray(coords)) return [];
14912
- if (typeof coords[0] === "number" && typeof coords[1] === "number") return [];
14913
- if (Array.isArray(coords[0]) && typeof coords[0][0] === "number") {
14914
- return [[coords]];
14915
- }
14916
- if (Array.isArray(coords[0]) && Array.isArray(coords[0][0]) && typeof coords[0][0][0] === "number") {
14917
- return [coords];
14918
- }
14919
- if (Array.isArray(coords[0]) && Array.isArray(coords[0][0]) && Array.isArray(coords[0][0][0]) && typeof coords[0][0][0][0] === "number") {
14920
- return coords;
14921
- }
14922
- return [];
14923
- };
14924
- const polys = normalize(coordinates);
14925
- if (polys.length === 0) return { proximity: false, point: [0, 0], distance: null };
14926
- const lon = point[0];
14927
- const lat = point[1];
14928
- const pointInRing = (pt, ring) => {
14929
- let x = pt[0], y = pt[1];
14930
- let inside = false;
14931
- for (let i = 0, j2 = ring.length - 1; i < ring.length; j2 = i++) {
14932
- const xi = ring[i][0], yi = ring[i][1];
14933
- const xj = ring[j2][0], yj = ring[j2][1];
14934
- const intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi + 0) + xi;
14935
- if (intersect) inside = !inside;
14936
- }
14937
- return inside;
14938
- };
14939
- const toRadians = (deg) => deg * Math.PI / 180;
14940
- const haversineMiles = (a, b) => {
14941
- const R = 3958.8;
14942
- const dLat = toRadians(b[1] - a[1]);
14943
- const dLon = toRadians(b[0] - a[0]);
14944
- const lat1 = toRadians(a[1]);
14945
- const lat2 = toRadians(b[1]);
14946
- const sinDLat = Math.sin(dLat / 2);
14947
- const sinDLon = Math.sin(dLon / 2);
14948
- const c = 2 * Math.asin(Math.sqrt(sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon));
14949
- return R * c;
14950
- };
14951
- let minDistance = Infinity;
14952
- let closestPoint = null;
14953
- for (const poly of polys) {
14954
- const outer = poly[0];
14955
- const holes = poly.slice(1);
14956
- if (pointInRing(point, outer)) {
14957
- let inHole = false;
14958
- for (const hole of holes) {
14959
- if (pointInRing(point, hole)) {
14960
- inHole = true;
14961
- break;
14962
- }
14963
- }
14964
- if (!inHole) {
14965
- return { proximity: true, point, distance: 0 };
14966
- }
14967
- }
14968
- for (const ring of poly) {
14969
- for (let i = 0; i < ring.length - 1; i++) {
14970
- const start = [ring[i][0], ring[i][1]];
14971
- const end = [ring[i + 1][0], ring[i + 1][1]];
14972
- const A = lon - start[0];
14973
- const B = lat - start[1];
14974
- const C = end[0] - start[0];
14975
- const D = end[1] - start[1];
14976
- const lenSq = C * C + D * D;
14977
- const t = lenSq === 0 ? 0 : Math.max(0, Math.min(1, (A * C + B * D) / lenSq));
14978
- const candidate = [start[0] + t * C, start[1] + t * D];
14979
- const dist = haversineMiles([lon, lat], candidate);
14980
- if (!isNaN(dist) && dist < minDistance) {
14981
- minDistance = Number(dist.toFixed(3));
14982
- closestPoint = candidate;
14983
- }
14984
- }
14985
- }
14806
+ let fmt = null;
14807
+ let data = null;
14808
+ let i = 12;
14809
+ while (i + 8 <= buffer.length) {
14810
+ const id2 = buffer.toString("ascii", i, i + 4);
14811
+ const size = buffer.readUInt32LE(i + 4);
14812
+ const start = i + 8;
14813
+ const end = start + size;
14814
+ if (id2 === "fmt ") fmt = buffer.slice(start, end);
14815
+ if (id2 === "data") data = buffer.slice(start, end);
14816
+ i = end + size % 2;
14986
14817
  }
14987
- if (!isFinite(minDistance) || closestPoint == null) {
14988
- return { proximity: false, point: [0, 0], distance: null };
14818
+ if (!fmt || !data) return null;
14819
+ const audioFormat = fmt.readUInt16LE(0);
14820
+ const channels = fmt.readUInt16LE(2);
14821
+ const sampleRate = fmt.readUInt32LE(4);
14822
+ const bitsPerSample = fmt.readUInt16LE(14);
14823
+ if (audioFormat !== 1 || bitsPerSample !== 16 || channels !== 1) {
14824
+ return null;
14989
14825
  }
14990
- const distanceMiles = minDistance;
14991
- const distanceKm = Number((distanceMiles * 1.609344).toFixed(3));
14992
- const distanceMeters = Math.round(distanceKm * 1e3);
14993
- return { proximity: distanceMiles === 0, point: closestPoint, distance: distanceMiles, distanceKm, distanceMeters };
14826
+ const samples = new Int16Array(data.buffer, data.byteOffset, data.length / 2);
14827
+ return { samples: new Int16Array(samples), sampleRate, channels, bitsPerSample };
14994
14828
  };
14995
14829
 
14996
- // src/@building/building.polygon.ts
14997
- var getEventNodes = (event) => __async(null, null, function* () {
14998
- var _a, _b;
14999
- const metadata = { nodes: [], proximity: false, filtered: false };
15000
- const geometry = yield getEventGeometry(event);
15001
- if (!geometry || !geometry.coordinates) {
15002
- return { nodes: [], filtered: false, updated: Date.now() };
14830
+ // src/@modules/@eas/eas.getSampledPCM16.ts
14831
+ var getSampledPCM16 = (int16, originalRate, targetRate) => {
14832
+ if (originalRate === targetRate) return int16;
14833
+ const ratio = targetRate / originalRate;
14834
+ const outLen = Math.max(1, Math.round(int16.length * ratio));
14835
+ const out = new Int16Array(outLen);
14836
+ for (let i = 0; i < outLen; i++) {
14837
+ const pos = i / ratio;
14838
+ const i0 = Math.floor(pos);
14839
+ const i1 = Math.min(i0 + 1, int16.length - 1);
14840
+ const frac = pos - i0;
14841
+ const v = int16[i0] * (1 - frac) + int16[i1] * frac;
14842
+ out[i] = Math.round(v);
15003
14843
  }
15004
- const nodes = bootstrap.cache.nodes.features;
15005
- for (const node of nodes) {
15006
- const [longitude, latitude] = node.geometry.coordinates;
15007
- const getPoint = getShapeNearestPoint(geometry.coordinates, [longitude, latitude]);
15008
- const miles = (_a = getPoint.distance) != null ? _a : null;
15009
- const kilometers = Number((miles * 1.609344).toFixed(3));
15010
- const info = {
15011
- id: (_b = node.properties) == null ? void 0 : _b.identifier,
15012
- coordinates: [longitude, latitude],
15013
- nearest: getPoint.point,
15014
- miles,
15015
- kilometers,
15016
- proximity: getPoint.proximity
15017
- };
15018
- metadata.nodes.push(info);
15019
- if (bootstrap.settings.GlobalSettings.EventFiltering.NodeLocationFiltering && miles < bootstrap.settings.GlobalSettings.NodeMinDistance) {
15020
- metadata.proximity = true;
15021
- info.proximity = true;
15022
- }
14844
+ return out;
14845
+ };
14846
+
14847
+ // src/@modules/@eas/eas.getPCMToFloat.ts
14848
+ var getPCMToFloat = (int16) => {
14849
+ const out = new Float32Array(int16.length);
14850
+ for (let i = 0; i < int16.length; i++) out[i] = int16[i] / 32768;
14851
+ return out;
14852
+ };
14853
+
14854
+ // src/@modules/@eas/eas.getFloatPCM16.ts
14855
+ var getFloatPCM16 = (float32) => {
14856
+ const out = new Int16Array(float32.length);
14857
+ for (let i = 0; i < float32.length; i++) {
14858
+ let v = Math.max(-1, Math.min(1, float32[i]));
14859
+ out[i] = Math.round(v * 32767);
15023
14860
  }
15024
- return {
15025
- nodes: metadata.nodes,
15026
- filtered: metadata.proximity,
15027
- updated: Date.now()
15028
- };
15029
- });
14861
+ return out;
14862
+ };
15030
14863
 
15031
- // src/@manager/manager.updateNodes.ts
15032
- var updateNode = (selectedEvent) => __async(null, null, function* () {
15033
- const events = bootstrap.cache.events.features;
15034
- const ttl = bootstrap.settings.GlobalSettings.NodeTTL * 1e3;
15035
- let total = 0;
15036
- function update(evt) {
15037
- return __async(this, null, function* () {
15038
- var _a, _b, _c;
15039
- const lastUpdate = (_c = (_b = (_a = evt == null ? void 0 : evt.properties) == null ? void 0 : _a.metadata) == null ? void 0 : _b.updated) != null ? _c : null;
15040
- if (lastUpdate != null && Date.now() - lastUpdate < ttl) {
15041
- return evt;
15042
- }
15043
- const node = yield getEventNodes(evt);
15044
- if (node.nodes.length > 0) {
15045
- total++;
15046
- }
15047
- evt.properties.metadata.nodes = node.nodes;
15048
- evt.properties.metadata.filtered_proximity = node.filtered;
15049
- evt.properties.metadata.updated = node.updated;
15050
- });
14864
+ // src/@modules/@eas/eas.setRadioEffect.ts
14865
+ var setRadioEffect = (int16, sampleRate) => {
14866
+ const hpCut = 3555;
14867
+ const lpCut = 1600;
14868
+ const x = getPCMToFloat(int16);
14869
+ const dt = 1 / sampleRate;
14870
+ const rcHP = 1 / (2 * Math.PI * hpCut);
14871
+ const aHP = rcHP / (rcHP + dt);
14872
+ let yHP = 0, xPrev = 0;
14873
+ for (let i = 0; i < x.length; i++) {
14874
+ const xi = x[i];
14875
+ yHP = aHP * (yHP + xi - xPrev);
14876
+ xPrev = xi;
14877
+ x[i] = yHP;
15051
14878
  }
15052
- if (!selectedEvent) {
15053
- yield Promise.all(events.map((evt) => __async(null, null, function* () {
14879
+ const rcLP = 1 / (2 * Math.PI * lpCut);
14880
+ const aLP = dt / (rcLP + dt);
14881
+ let yLP = 0;
14882
+ for (let i = 0; i < x.length; i++) {
14883
+ yLP = yLP + aLP * (x[i] - yLP);
14884
+ x[i] = yLP;
14885
+ }
14886
+ const compGain = 2;
14887
+ const norm = Math.tanh(compGain);
14888
+ for (let i = 0; i < x.length; i++) x[i] = Math.tanh(x[i] * compGain) / norm;
14889
+ return getFloatPCM16(x);
14890
+ };
14891
+
14892
+ // src/@modules/@eas/eas.setAFSK.ts
14893
+ var setAFSK = (bits, sampleRate) => {
14894
+ const baud = 520.83;
14895
+ const markFreq = 2083.3;
14896
+ const spaceFreq = 1562.5;
14897
+ const amplitude = 0.6;
14898
+ const twoPi = Math.PI * 2;
14899
+ const result = [];
14900
+ let phase = 0;
14901
+ let frac = 0;
14902
+ for (let b = 0; b < bits.length; b++) {
14903
+ const bit = bits[b];
14904
+ const freq = bit ? markFreq : spaceFreq;
14905
+ const samplesPerBit = sampleRate / baud + frac;
14906
+ const n = Math.round(samplesPerBit);
14907
+ frac = samplesPerBit - n;
14908
+ const inc = twoPi * freq / sampleRate;
14909
+ for (let i = 0; i < n; i++) {
14910
+ result.push(Math.round(Math.sin(phase) * amplitude * 32767));
14911
+ phase += inc;
14912
+ if (phase > twoPi) phase -= twoPi;
14913
+ }
14914
+ }
14915
+ const fadeSamples = Math.floor(sampleRate * 2e-3);
14916
+ for (let i = 0; i < fadeSamples; i++) {
14917
+ const gain = i / fadeSamples;
14918
+ result[i] = Math.round(result[i] * gain);
14919
+ result[result.length - 1 - i] = Math.round(result[result.length - 1 - i] * gain);
14920
+ }
14921
+ return Int16Array.from(result);
14922
+ };
14923
+
14924
+ // src/@modules/@eas/eas.setAsciiToBits.ts
14925
+ var setAsciiToBits = (str) => {
14926
+ const bits = [];
14927
+ for (let i = 0; i < str.length; i++) {
14928
+ const c = str.charCodeAt(i) & 255;
14929
+ bits.push(0);
14930
+ for (let b = 0; b < 8; b++) bits.push(c >> b & 1);
14931
+ bits.push(1, 1);
14932
+ }
14933
+ return bits;
14934
+ };
14935
+
14936
+ // src/@modules/@eas/eas.getMergedPCM16.ts
14937
+ var getMergedPCM16 = (arrays) => {
14938
+ let total = 0;
14939
+ for (const a of arrays) total += a.length;
14940
+ const out = new Int16Array(total);
14941
+ let o = 0;
14942
+ for (const a of arrays) {
14943
+ out.set(a, o);
14944
+ o += a.length;
14945
+ }
14946
+ return out;
14947
+ };
14948
+
14949
+ // src/@modules/@eas/eas.setSameHeader.ts
14950
+ var setSameHeader = (vtec2, repeats, sampleRate = 8e3, options = {}) => {
14951
+ var _a, _b;
14952
+ const preMarkSec = (_a = options.preMarkSec) != null ? _a : 0.3;
14953
+ const gapSec = (_b = options.gapSec) != null ? _b : 0.1;
14954
+ const bursts = [];
14955
+ const gap = new Int16Array(Math.floor(gapSec * sampleRate));
14956
+ for (let i = 0; i < repeats; i++) {
14957
+ const bodyBits = setAsciiToBits(vtec2);
14958
+ const body = setAFSK(bodyBits, sampleRate);
14959
+ const extendedBodyDuration = Math.round(preMarkSec * sampleRate);
14960
+ const extendedBody = new Int16Array(extendedBodyDuration + gap.length);
14961
+ for (let j2 = 0; j2 < extendedBodyDuration; j2++) {
14962
+ extendedBody[j2] = Math.round(body[j2 % body.length] * 0.2);
14963
+ }
14964
+ extendedBody.set(gap, extendedBodyDuration);
14965
+ bursts.push(extendedBody);
14966
+ if (i !== repeats - 1) bursts.push(gap);
14967
+ }
14968
+ return getMergedPCM16(bursts);
14969
+ };
14970
+
14971
+ // src/@modules/@eas/eas.setAttentionTone.ts
14972
+ var setAttentionTone = (ms, sampleRate) => {
14973
+ const len = Math.floor(ms * sampleRate);
14974
+ const out = new Int16Array(len);
14975
+ const f1 = 853;
14976
+ const f2 = 960;
14977
+ const twoPi = Math.PI * 2;
14978
+ const amp = 0.1;
14979
+ const fadeLen = Math.floor(sampleRate * 0);
14980
+ for (let i = 0; i < len; i++) {
14981
+ const t = i / sampleRate;
14982
+ const s = Math.sin(twoPi * f1 * t) + Math.sin(twoPi * f2 * t);
14983
+ let gain = 1;
14984
+ if (i < fadeLen) gain = i / fadeLen;
14985
+ else if (i > len - fadeLen) gain = (len - i) / fadeLen;
14986
+ const v = Math.max(-1, Math.min(1, s / 2 * amp * gain));
14987
+ out[i] = Math.round(v * 32767);
14988
+ }
14989
+ return out;
14990
+ };
14991
+
14992
+ // src/@modules/@eas/eas.setNoise.ts
14993
+ var setNoise = (int16, noiseLevel = 0.02) => {
14994
+ const x = getPCMToFloat(int16);
14995
+ for (let i = 0; i < x.length; i++) x[i] += (Math.random() * 2 - 1) * noiseLevel;
14996
+ let peak = 0;
14997
+ for (let i = 0; i < x.length; i++) peak = Math.max(peak, Math.abs(x[i]));
14998
+ if (peak > 1) for (let i = 0; i < x.length; i++) x[i] *= 0.98 / peak;
14999
+ return getFloatPCM16(x);
15000
+ };
15001
+
15002
+ // src/@modules/@eas/eas.getPCM16.ts
15003
+ var getPCM16 = (samples, sampleRate) => {
15004
+ let o = 0;
15005
+ const bytesPerSample = 2;
15006
+ const blockAlign = 1 * bytesPerSample;
15007
+ const byteRate = sampleRate * blockAlign;
15008
+ const subchunk2Size = samples.length * bytesPerSample;
15009
+ const chunkSize = 36 + subchunk2Size;
15010
+ const buffer = Buffer.alloc(44 + subchunk2Size);
15011
+ buffer.write("RIFF", o);
15012
+ o += 4;
15013
+ buffer.writeUInt32LE(chunkSize, o);
15014
+ o += 4;
15015
+ buffer.write("WAVE", o);
15016
+ o += 4;
15017
+ buffer.write("fmt ", o);
15018
+ o += 4;
15019
+ buffer.writeUInt32LE(16, o);
15020
+ o += 4;
15021
+ buffer.writeUInt16LE(1, o);
15022
+ o += 2;
15023
+ buffer.writeUInt16LE(1, o);
15024
+ o += 2;
15025
+ buffer.writeUInt32LE(sampleRate, o);
15026
+ o += 4;
15027
+ buffer.writeUInt32LE(byteRate, o);
15028
+ o += 4;
15029
+ buffer.writeUInt16LE(blockAlign, o);
15030
+ o += 2;
15031
+ buffer.writeUInt16LE(16, o);
15032
+ o += 2;
15033
+ buffer.write("data", o);
15034
+ o += 4;
15035
+ buffer.writeUInt32LE(subchunk2Size, o);
15036
+ o += 4;
15037
+ for (let i = 0; i < samples.length; i++, o += 2) {
15038
+ buffer.writeInt16LE(samples[i].value, o);
15039
+ }
15040
+ return buffer;
15041
+ };
15042
+
15043
+ // src/@modules/@eas/eas.getTTS.ts
15044
+ import { platform } from "os";
15045
+ import { execSync } from "child_process";
15046
+ var getTTS = (text2, outputPath) => __async(null, null, function* () {
15047
+ const vPlatform = platform();
15048
+ switch (vPlatform) {
15049
+ case "win32": {
15050
+ const command = `Add-Type -AssemblyName System.Speech;$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer;$speak.SetOutputToWaveFile('${outputPath}');$speak.Speak('${text2}');$speak.Dispose();`;
15051
+ execSync(`powershell -Command "${command}"`, { stdio: "ignore" });
15052
+ break;
15053
+ }
15054
+ case "linux":
15055
+ execSync(`espeak -w "${outputPath}" "${text2.replace(/"/g, '\\"')}"`);
15056
+ break;
15057
+ default:
15058
+ break;
15059
+ }
15060
+ });
15061
+
15062
+ // src/@modules/@eas/eas.getCleanDescription.ts
15063
+ var getCleanDescription = (message) => {
15064
+ const patches = [
15065
+ { regex: /\.{3,}/g, replacement: ". " },
15066
+ { regex: /\bUTC\b/g, replacement: "Coordinated Universal Time" },
15067
+ { regex: /\bGMT\b/g, replacement: "Greenwich Mean Time" },
15068
+ { regex: /\bEST\b(?!\w)/g, replacement: "Eastern Standard Time" },
15069
+ { regex: /\bEDT\b(?!\w)/g, replacement: "Eastern Daylight Time" },
15070
+ { regex: /\bCST\b(?!\w)/g, replacement: "Central Standard Time" },
15071
+ { regex: /\bCDT\b(?!\w)/g, replacement: "Central Daylight Time" },
15072
+ { regex: /\bMST\b(?!\w)/g, replacement: "Mountain Standard Time" },
15073
+ { regex: /\bMDT\b(?!\w)/g, replacement: "Mountain Daylight Time" },
15074
+ { regex: /\bPST\b(?!\w)/g, replacement: "Pacific Standard Time" },
15075
+ { regex: /\bPDT\b(?!\w)/g, replacement: "Pacific Daylight Time" },
15076
+ { regex: /\bAKST\b(?!\w)/g, replacement: "Alaska Standard Time" },
15077
+ { regex: /\bAKDT\b(?!\w)/g, replacement: "Alaska Daylight Time" },
15078
+ { regex: /\bHST\b(?!\w)/g, replacement: "Hawaii Standard Time" },
15079
+ { regex: /\bHDT\b(?!\w)/g, replacement: "Hawaii Daylight Time" },
15080
+ { regex: /\bmph\b(?!\w)/g, replacement: "miles per hour" },
15081
+ { regex: /\bkm\/h\b(?!\w)/g, replacement: "kilometers per hour" },
15082
+ { regex: /\bkmh\b(?!\w)/g, replacement: "kilometers per hour" },
15083
+ { regex: /\bkt\b(?!\w)/g, replacement: "knots" },
15084
+ { regex: /\bNE\b(?!\w)/g, replacement: "northeast" },
15085
+ { regex: /\bNW\b(?!\w)/g, replacement: "northwest" },
15086
+ { regex: /\bSE\b(?!\w)/g, replacement: "southeast" },
15087
+ { regex: /\bSW\b(?!\w)/g, replacement: "southwest" },
15088
+ { regex: /\bNM\b(?!\w)/g, replacement: "nautical miles" },
15089
+ { regex: /\bdeg\b(?!\w)/g, replacement: "degrees" },
15090
+ { regex: /\btstm\b(?!\w)/g, replacement: "thunderstorm" },
15091
+ { regex: /\bmm\b(?!\w)/g, replacement: "millimeters" },
15092
+ { regex: /\bcm\b(?!\w)/g, replacement: "centimeters" },
15093
+ { regex: /\bin.\b(?!\w)/g, replacement: "inches" },
15094
+ { regex: /\bft\b(?!\w)/g, replacement: "feet" },
15095
+ { regex: /\bmi\b(?!\w)/g, replacement: "miles" },
15096
+ { regex: /\bhr\b(?!\w)/g, replacement: "hour" },
15097
+ { regex: /\bhourly\b(?!\w)/g, replacement: "per hour" },
15098
+ { regex: /\bkg\b(?!\w)/g, replacement: "kilograms" },
15099
+ { regex: /\bg\/kg\b(?!\w)/g, replacement: "grams per kilogram" },
15100
+ { regex: /\bmb\b(?!\w)/g, replacement: "millibars" },
15101
+ { regex: /\bhPa\b(?!\w)/g, replacement: "hectopascals" },
15102
+ { regex: /\bPa\b(?!\w)/g, replacement: "pascals" },
15103
+ { regex: /\bKPa\b(?!\w)/g, replacement: "kilopascals" },
15104
+ { regex: /\bC\/hr\b(?!\w)/g, replacement: "degrees Celsius per hour" },
15105
+ { regex: /\bF\/hr\b(?!\w)/g, replacement: "degrees Fahrenheit per hour" },
15106
+ { regex: /\bC\/min\b(?!\w)/g, replacement: "degrees Celsius per minute" },
15107
+ { regex: /\bF\/min\b(?!\w)/g, replacement: "degrees Fahrenheit per minute" },
15108
+ { regex: /\bC\b(?!\w)/g, replacement: "degrees Celsius" },
15109
+ { regex: /\bF\b(?!\w)/g, replacement: "degrees Fahrenheit" }
15110
+ ];
15111
+ const hours = {
15112
+ 0: "twelve",
15113
+ 1: "one",
15114
+ 2: "two",
15115
+ 3: "three",
15116
+ 4: "four",
15117
+ 5: "five",
15118
+ 6: "six",
15119
+ 7: "seven",
15120
+ 8: "eight",
15121
+ 9: "nine",
15122
+ 10: "ten",
15123
+ 11: "eleven",
15124
+ 12: "twelve"
15125
+ };
15126
+ const minWord = (m) => m === 0 ? "o'clock" : m < 10 ? `oh ${m}` : `${m}`;
15127
+ const fmt = (h, m, ap) => `${hours[h % 12]} ${minWord(m)} ${ap}`;
15128
+ for (const { regex, replacement } of patches) {
15129
+ message = message.replace(regex, replacement);
15130
+ }
15131
+ return message.replace(/\b(\d{1,2}):(\d{2})\s*(AM|PM)\b/gi, (_, h, m, ap) => fmt(+h, +m, ap.toUpperCase())).replace(/\b(\d{1,2})\s+(\d{2})\s+(AM|PM)\b/gi, (_, h, m, ap) => fmt(+h, +m, ap.toUpperCase())).replace(/\b(\d{2})(\d{2})\s+(AM|PM)\b/gi, (_, hh, mm, ap) => fmt(+hh, +mm, ap.toUpperCase())).replace(/\*+/g, " ").replace(/-/g, " ").replace(/\n/g, " ").trim();
15132
+ };
15133
+
15134
+ // src/@modules/@eas/eas.setEasTone.ts
15135
+ import { join } from "path";
15136
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
15137
+ import { execSync as execSync2 } from "child_process";
15138
+ import { platform as platform2 } from "os";
15139
+ var setEasTone = (options) => __async(null, null, function* () {
15140
+ const settings = bootstrap.settings;
15141
+ const directory = settings.GlobalSettings.EASSettings.ArchiveDirectory;
15142
+ const prefix = settings.GlobalSettings.EASSettings.IntroWavFile;
15143
+ let message = options.message;
15144
+ let header = options.header;
15145
+ if (!message || !header) {
15146
+ setWarning({
15147
+ title: `EAS`,
15148
+ message: `Message and header are required to generate an EAS tone.`
15149
+ });
15150
+ return null;
15151
+ }
15152
+ let buffTTS;
15153
+ let buffRadio;
15154
+ let buffFull = [];
15155
+ if (!existsSync(directory)) {
15156
+ mkdirSync(directory, { recursive: true });
15157
+ }
15158
+ if (!existsSync(join(directory, `/temp`))) {
15159
+ mkdirSync(join(directory, `/temp`), { recursive: true });
15160
+ }
15161
+ if (!existsSync(join(directory, `/output`))) {
15162
+ mkdirSync(join(directory, `/output`), { recursive: true });
15163
+ }
15164
+ const tmpTTS = join(directory, `/temp/${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, "")}.wav`);
15165
+ const outTTS = join(directory, `/output/${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, "")}.wav`);
15166
+ const vPlatform = platform2();
15167
+ if (vPlatform === "darwin") {
15168
+ setWarning({
15169
+ title: `EAS`,
15170
+ message: `EAS tone generation is not supported on macOS.`
15171
+ });
15172
+ return null;
15173
+ }
15174
+ yield new Promise((resolve6, reject) => {
15175
+ const tMsg = getCleanDescription(message);
15176
+ getTTS(tMsg, tmpTTS).then(() => {
15177
+ buffTTS = readFileSync(tmpTTS);
15178
+ resolve6();
15179
+ });
15180
+ });
15181
+ const vWav = getWavPCM16(buffTTS);
15182
+ const vSamples = getSampledPCM16(vWav.samples, vWav.sampleRate, 8e3);
15183
+ const vRadio = setRadioEffect(vSamples, 8e3);
15184
+ if (existsSync(prefix)) {
15185
+ let tBuffer = readFileSync(prefix);
15186
+ let tWav = getWavPCM16(tBuffer);
15187
+ if (tWav == null) {
15188
+ try {
15189
+ const converted = join(directory, `/temp/${Math.random().toString(36).substring(2, 15)}.converted.wav`);
15190
+ execSync2(`ffmpeg -y -i "${prefix}" -ar 8000 -ac 1 -sample_fmt s16 "${converted}"`, { stdio: "ignore" });
15191
+ if (existsSync(converted)) {
15192
+ tBuffer = readFileSync(converted);
15193
+ tWav = getWavPCM16(tBuffer);
15194
+ try {
15195
+ unlinkSync(converted);
15196
+ } catch (e) {
15197
+ }
15198
+ }
15199
+ } catch (e) {
15200
+ }
15201
+ }
15202
+ if (tWav == null) {
15203
+ setWarning({ title: `EAS`, message: `Intro tone isn't a valid .WAV file or isn't in PCM 16-bit format. Converted attempt failed; please convert it then try again.` });
15204
+ return null;
15205
+ }
15206
+ const tSamples = tWav.sampleRate != 8e3 ? getSampledPCM16(tWav.samples, tWav.sampleRate, 8e3) : tWav.samples;
15207
+ buffRadio = setRadioEffect(tSamples, 8e3);
15208
+ }
15209
+ buffFull = buffRadio != null ? [buffRadio, new Int16Array(Math.floor(0.5 * 8e3))] : [];
15210
+ buffFull.push(
15211
+ setSameHeader(header, 3, 8e3, {
15212
+ preMarkSec: 1.1,
15213
+ gapSec: 0.5
15214
+ }),
15215
+ new Int16Array(Math.floor(0.5 * 8e3)),
15216
+ setAttentionTone(8, 8e3),
15217
+ new Int16Array(Math.floor(0.5 * 8e3)),
15218
+ vRadio
15219
+ );
15220
+ for (let i = 0; i < 3; i++) {
15221
+ buffFull.push(setSameHeader(header, 1, 8e3, { preMarkSec: 0.5, gapSec: 0.1 }));
15222
+ buffFull.push(new Int16Array(Math.floor(0.5 * 8e3)));
15223
+ }
15224
+ const aSamples = getMergedPCM16(buffFull);
15225
+ const aFinal = setNoise(aSamples, 2e-3);
15226
+ const aBuffer = getPCM16(Array.from(aFinal).map((v) => ({ value: v })), 8e3);
15227
+ writeFileSync(outTTS, aBuffer);
15228
+ try {
15229
+ unlinkSync(tmpTTS);
15230
+ } catch (e) {
15231
+ }
15232
+ return outTTS;
15233
+ });
15234
+
15235
+ // src/@manager/manager.createWebhook.ts
15236
+ var import_form_data = __toESM(require_form_data());
15237
+ import { readFileSync as readFileSync2 } from "fs";
15238
+ var createWebhook = (options) => __async(null, null, function* () {
15239
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
15240
+ const event = options.event.properties;
15241
+ const settings = options.webhook;
15242
+ const line = (label, value, condition = true) => condition && value ? `${label} ${value}` : null;
15243
+ const isLimited = setTimeoutAction({ identifier: options.webhook.webhook, interval: options.webhook.rate, max: options.webhook.rate, addTime: true });
15244
+ if (!isLimited.limited) {
15245
+ const isStatement = event.status_metadata.is_statement;
15246
+ const isExpired = event.status_metadata.is_expired;
15247
+ let body = [
15248
+ line(`**Locations:**`, (_a = event == null ? void 0 : event.locations) == null ? void 0 : _a.slice(0, 100)),
15249
+ line(`**Issued:**`, `<t:${Math.floor(new Date(event.issued).getTime() / 1e3)}:R>`),
15250
+ line(`**Expires:**`, `<t:${Math.floor(new Date(event.expires).getTime() / 1e3)}:R>`, !isExpired && !isStatement),
15251
+ line(`**Damage Threat:**`, event.parameters.damage_threat, !isExpired),
15252
+ line(`**Flood Threat:**`, event.parameters.flood_threat, !isExpired),
15253
+ line(`**Tornado Threat:**`, event.parameters.tornado_threat, !isExpired),
15254
+ line(`**Estimated Wind Gusts:**`, `${event.parameters.estimated_wind_gusts} ${event.parameters.wind_threat ? ` (${event.parameters.wind_threat})` : ""}`, !isExpired && event.parameters.estimated_wind_gusts != null),
15255
+ line(`**Estimated Hail Size:**`, `${event.parameters.estimated_hail_size} ${event.parameters.hail_threat ? ` (${event.parameters.hail_threat})` : ""}`, !isExpired && event.parameters.estimated_hail_size != null),
15256
+ line(`**Discussion:**`, event.spc_parameters.spc_number, !isExpired),
15257
+ line(`**Concern:**`, event.spc_parameters.spc_concerning, !isExpired),
15258
+ line(`**SPC Max Tornado Threat:**`, event.spc_parameters.spc_max_tornado, !isExpired),
15259
+ line(`**SPC Max Hail Threat:**`, event.spc_parameters.spc_max_hail, !isExpired),
15260
+ line(`**SPC Max Wind Threat:**`, event.spc_parameters.spc_max_wind, !isExpired),
15261
+ line(`**SPC Watch Issuance Probability:**`, event.spc_parameters.spc_watch_issuance ? `${event.spc_parameters.spc_watch_issuance}%` : null, !isExpired),
15262
+ line(`**Watch Number:**`, event.watch_parameters.watch_number, !isExpired),
15263
+ line(`**Strong Tornadoes Probability:**`, event.watch_parameters.strong_tornadoes_probability ? `${event.watch_parameters.strong_tornadoes_probability}%` : null, !isExpired),
15264
+ line(`**Additional Tornadoes Probability:**`, event.watch_parameters.additional_tornadoes_probability ? `${event.watch_parameters.additional_tornadoes_probability}%` : null, !isExpired),
15265
+ line(`**Combined Hail/Wind Probability:**`, event.watch_parameters.combined_hail_wind_probability ? `${event.watch_parameters.combined_hail_wind_probability}%` : null, !isExpired),
15266
+ line(`**Severe Hail Probability:**`, event.watch_parameters.severe_hail_probability ? `${event.watch_parameters.severe_hail_probability}%` : null, !isExpired),
15267
+ line(`**Hail >2in Probability:**`, event.watch_parameters.hail_2in_probability ? `${event.watch_parameters.hail_2in_probability}%` : null, !isExpired),
15268
+ line(`**Max Hail Inches:**`, event.watch_parameters.max_hail_in, !isExpired),
15269
+ line(`**Severe Wind Probability:**`, event.watch_parameters.severe_wind_probability ? `${event.watch_parameters.severe_wind_probability}%` : null, !isExpired),
15270
+ line(`**Max Surface Wind:**`, event.watch_parameters.max_wind_surface, !isExpired),
15271
+ line(`**Max Tops (x100 feet):**`, event.watch_parameters.max_tops_x100feet, !isExpired),
15272
+ line(`**Tags:**`, ((_b = event.parameters.tags) == null ? void 0 : _b.length) > 0 ? event.parameters.tags.join(", ") : null, !isExpired),
15273
+ line(`**Sender:**`, ((_d = (_c = event.geocode) == null ? void 0 : _c.office) == null ? void 0 : _d.name) ? `${event.geocode.office.name} (${event.geocode.office.office})` : (_f = (_e = event.geocode) == null ? void 0 : _e.office) == null ? void 0 : _f.office),
15274
+ line(`**Tracking:**`, (_g = event.metadata) == null ? void 0 : _g.tracking),
15275
+ line(`**Logs:**`, ((_h = event.metadata.history) == null ? void 0 : _h.length) > 0 ? event.metadata.history.length : null),
15276
+ line(``, event.description ? "```\n" + event.description.split("\n").map((l) => l.trim()).filter(Boolean).join("\n") + "\n```" : null, !isExpired)
15277
+ ].filter(Boolean).join("\n");
15278
+ if (body.length > 1900) {
15279
+ body = body.substring(0, 1900) + "\n\n[Message truncated due to length]";
15280
+ const blocks = ((_i = body.match(/```/g)) != null ? _i : []).length;
15281
+ if (blocks % 2 !== 0) body += "```";
15282
+ }
15283
+ if (event.description.length < 25) {
15284
+ return;
15285
+ }
15286
+ const form = new import_form_data.default();
15287
+ const img = (_j = event.metadata.attachments) == null ? void 0 : _j[0];
15288
+ const embed = {
15289
+ title: `${event.event} (${event.status})`,
15290
+ description: body,
15291
+ color: 16711680,
15292
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
15293
+ image: {},
15294
+ footer: { text: settings.title }
15295
+ };
15296
+ if (img) {
15297
+ for (let i = 0; i < 5; i++) {
15298
+ const ok = yield isImageReady(img);
15299
+ if (ok) break;
15300
+ yield new Promise((r) => setTimeout(r, 5e3));
15301
+ }
15302
+ const finalCheck = yield isImageReady(img);
15303
+ if (finalCheck) {
15304
+ embed.image = { url: img };
15305
+ }
15306
+ }
15307
+ form.append("payload_json", JSON.stringify({
15308
+ username: (_k = settings.title) != null ? _k : "AtmosphericX",
15309
+ content: (_l = settings.message) != null ? _l : "",
15310
+ embeds: [embed]
15311
+ }));
15312
+ if (settings.upload) {
15313
+ form.append("fUpload", Buffer.from(`${event.metadata.raw}
15314
+
15315
+ ${JSON.stringify(getCleanedEvent(event), null, 2)}`), { filename: `${event.event}_${event.status}_${event.metadata.tracking}.txt`, contentType: "application/text" });
15316
+ }
15317
+ if (settings.eas) {
15318
+ const audio = yield setEasTone({
15319
+ message: event.description,
15320
+ header: event.metadata.header
15321
+ });
15322
+ const file = readFileSync2(audio);
15323
+ if (audio) {
15324
+ form.append("fEas", Buffer.from(file), { filename: `${event.event}_${event.status}_${event.metadata.tracking}_eas.mp3`, contentType: "audio/mpeg" });
15325
+ }
15326
+ }
15327
+ yield createHttp({
15328
+ url: settings.webhook,
15329
+ timeout: 2e3,
15330
+ method: `POST`,
15331
+ headers: form.getHeaders(),
15332
+ body: form
15333
+ });
15334
+ }
15335
+ });
15336
+
15337
+ // src/@manager/manager.updateWebhooks.ts
15338
+ var updateWebhooks = (event) => __async(null, null, function* () {
15339
+ const settings = bootstrap.settings;
15340
+ const webhooks = settings.WebhookSettings;
15341
+ const eventName = event.properties.event;
15342
+ for (const socket of webhooks) {
15343
+ const events = socket.events;
15344
+ if (!events || events.length === 0) {
15345
+ yield createWebhook({ webhook: socket, event });
15346
+ continue;
15347
+ }
15348
+ const matched = events.some((pattern) => {
15349
+ if (!pattern) return false;
15350
+ if (pattern === "*" || pattern === eventName) return true;
15351
+ if (pattern.includes("*")) {
15352
+ const regex = "^" + pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$";
15353
+ return new RegExp(regex).test(eventName);
15354
+ }
15355
+ return false;
15356
+ });
15357
+ if (matched) {
15358
+ yield createWebhook({ webhook: socket, event });
15359
+ }
15360
+ }
15361
+ });
15362
+
15363
+ // src/@modules/@utilities/utilities.getShapeNearestPoint.ts
15364
+ var getShapeNearestPoint = (coordinates, point) => {
15365
+ if (!coordinates || !point) {
15366
+ return { proximity: false, point: [0, 0], distance: null };
15367
+ }
15368
+ const normalize = (coords) => {
15369
+ if (!Array.isArray(coords)) return [];
15370
+ if (typeof coords[0] === "number" && typeof coords[1] === "number") return [];
15371
+ if (Array.isArray(coords[0]) && typeof coords[0][0] === "number") {
15372
+ return [[coords]];
15373
+ }
15374
+ if (Array.isArray(coords[0]) && Array.isArray(coords[0][0]) && typeof coords[0][0][0] === "number") {
15375
+ return [coords];
15376
+ }
15377
+ if (Array.isArray(coords[0]) && Array.isArray(coords[0][0]) && Array.isArray(coords[0][0][0]) && typeof coords[0][0][0][0] === "number") {
15378
+ return coords;
15379
+ }
15380
+ return [];
15381
+ };
15382
+ const polys = normalize(coordinates);
15383
+ if (polys.length === 0) return { proximity: false, point: [0, 0], distance: null };
15384
+ const lon = point[0];
15385
+ const lat = point[1];
15386
+ const pointInRing = (pt, ring) => {
15387
+ let x = pt[0], y = pt[1];
15388
+ let inside = false;
15389
+ for (let i = 0, j2 = ring.length - 1; i < ring.length; j2 = i++) {
15390
+ const xi = ring[i][0], yi = ring[i][1];
15391
+ const xj = ring[j2][0], yj = ring[j2][1];
15392
+ const intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi + 0) + xi;
15393
+ if (intersect) inside = !inside;
15394
+ }
15395
+ return inside;
15396
+ };
15397
+ const toRadians = (deg) => deg * Math.PI / 180;
15398
+ const haversineMiles = (a, b) => {
15399
+ const R = 3958.8;
15400
+ const dLat = toRadians(b[1] - a[1]);
15401
+ const dLon = toRadians(b[0] - a[0]);
15402
+ const lat1 = toRadians(a[1]);
15403
+ const lat2 = toRadians(b[1]);
15404
+ const sinDLat = Math.sin(dLat / 2);
15405
+ const sinDLon = Math.sin(dLon / 2);
15406
+ const c = 2 * Math.asin(Math.sqrt(sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon));
15407
+ return R * c;
15408
+ };
15409
+ let minDistance = Infinity;
15410
+ let closestPoint = null;
15411
+ for (const poly of polys) {
15412
+ const outer = poly[0];
15413
+ const holes = poly.slice(1);
15414
+ if (pointInRing(point, outer)) {
15415
+ let inHole = false;
15416
+ for (const hole of holes) {
15417
+ if (pointInRing(point, hole)) {
15418
+ inHole = true;
15419
+ break;
15420
+ }
15421
+ }
15422
+ if (!inHole) {
15423
+ return { proximity: true, point, distance: 0 };
15424
+ }
15425
+ }
15426
+ for (const ring of poly) {
15427
+ for (let i = 0; i < ring.length - 1; i++) {
15428
+ const start = [ring[i][0], ring[i][1]];
15429
+ const end = [ring[i + 1][0], ring[i + 1][1]];
15430
+ const A = lon - start[0];
15431
+ const B = lat - start[1];
15432
+ const C = end[0] - start[0];
15433
+ const D = end[1] - start[1];
15434
+ const lenSq = C * C + D * D;
15435
+ const t = lenSq === 0 ? 0 : Math.max(0, Math.min(1, (A * C + B * D) / lenSq));
15436
+ const candidate = [start[0] + t * C, start[1] + t * D];
15437
+ const dist = haversineMiles([lon, lat], candidate);
15438
+ if (!isNaN(dist) && dist < minDistance) {
15439
+ minDistance = Number(dist.toFixed(3));
15440
+ closestPoint = candidate;
15441
+ }
15442
+ }
15443
+ }
15444
+ }
15445
+ if (!isFinite(minDistance) || closestPoint == null) {
15446
+ return { proximity: false, point: [0, 0], distance: null };
15447
+ }
15448
+ const distanceMiles = minDistance;
15449
+ const distanceKm = Number((distanceMiles * 1.609344).toFixed(3));
15450
+ const distanceMeters = Math.round(distanceKm * 1e3);
15451
+ return { proximity: distanceMiles === 0, point: closestPoint, distance: distanceMiles, distanceKm, distanceMeters };
15452
+ };
15453
+
15454
+ // src/@building/building.polygon.ts
15455
+ var getEventNodes = (event) => __async(null, null, function* () {
15456
+ var _a, _b;
15457
+ const metadata = { nodes: [], proximity: false, filtered: false };
15458
+ const geometry = yield getEventGeometry(event);
15459
+ if (!geometry || !geometry.coordinates) {
15460
+ return { nodes: [], filtered: false, updated: Date.now() };
15461
+ }
15462
+ const nodes = bootstrap.cache.nodes.features;
15463
+ for (const node of nodes) {
15464
+ const [longitude, latitude] = node.geometry.coordinates;
15465
+ const getPoint = getShapeNearestPoint(geometry.coordinates, [longitude, latitude]);
15466
+ const miles = (_a = getPoint.distance) != null ? _a : null;
15467
+ const kilometers = Number((miles * 1.609344).toFixed(3));
15468
+ const info = {
15469
+ id: (_b = node.properties) == null ? void 0 : _b.identifier,
15470
+ coordinates: [longitude, latitude],
15471
+ nearest: getPoint.point,
15472
+ miles,
15473
+ kilometers,
15474
+ proximity: getPoint.proximity
15475
+ };
15476
+ metadata.nodes.push(info);
15477
+ if (bootstrap.settings.GlobalSettings.EventFiltering.NodeLocationFiltering && miles < bootstrap.settings.GlobalSettings.NodeMinDistance) {
15478
+ metadata.proximity = true;
15479
+ info.proximity = true;
15480
+ }
15481
+ }
15482
+ return {
15483
+ nodes: metadata.nodes,
15484
+ filtered: metadata.proximity,
15485
+ updated: Date.now()
15486
+ };
15487
+ });
15488
+
15489
+ // src/@manager/manager.updateNodes.ts
15490
+ var updateNode = (selectedEvent) => __async(null, null, function* () {
15491
+ const events = bootstrap.cache.events.features;
15492
+ const ttl = bootstrap.settings.GlobalSettings.NodeTTL * 1e3;
15493
+ let total = 0;
15494
+ function update(evt) {
15495
+ return __async(this, null, function* () {
15496
+ var _a, _b, _c;
15497
+ const lastUpdate = (_c = (_b = (_a = evt == null ? void 0 : evt.properties) == null ? void 0 : _a.metadata) == null ? void 0 : _b.updated) != null ? _c : null;
15498
+ if (lastUpdate != null && Date.now() - lastUpdate < ttl) {
15499
+ return evt;
15500
+ }
15501
+ const node = yield getEventNodes(evt);
15502
+ if (node.nodes.length > 0) {
15503
+ total++;
15504
+ }
15505
+ evt.properties.metadata.nodes = node.nodes;
15506
+ evt.properties.metadata.filtered_proximity = node.filtered;
15507
+ evt.properties.metadata.updated = node.updated;
15508
+ });
15509
+ }
15510
+ if (!selectedEvent) {
15511
+ yield Promise.all(events.map((evt) => __async(null, null, function* () {
15054
15512
  yield update(evt);
15055
15513
  })));
15056
15514
  }
@@ -15102,7 +15560,7 @@ var mkEvent = (event) => __async(null, null, function* () {
15102
15560
  const iHistory = (_o = (_n = (_m = event.properties) == null ? void 0 : _m.metadata) == null ? void 0 : _n.history) != null ? _o : [];
15103
15561
  const iLocations = (_r = (_q = (_p = event.properties) == null ? void 0 : _p.locations) == null ? void 0 : _q.split(";").map((l) => l.trim())) != null ? _r : [];
15104
15562
  const iUgc = (_u = (_t = (_s = event.properties) == null ? void 0 : _s.geocode) == null ? void 0 : _t.ugc) != null ? _u : [];
15105
- const mHistory = [...cHistory, ...iHistory].filter((v, i, a) => a.indexOf(v) === i);
15563
+ 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);
15106
15564
  const mLocations = [...cLocations, ...iLocations].filter((v, i, a) => a.indexOf(v) === i).join("; ");
15107
15565
  const mUgc = [...cUgc, ...iUgc].filter((v, i, a) => a.indexOf(v) === i);
15108
15566
  bootstrap.cache.events.features[getIndex] = __spreadProps(__spreadValues({}, event), {
@@ -15155,6 +15613,37 @@ var rmEvent = (event) => {
15155
15613
  });
15156
15614
  };
15157
15615
 
15616
+ // src/@modules/@utilities/utilities.getLatestIssuance.ts
15617
+ var getLatestIssuance = () => {
15618
+ const now = /* @__PURE__ */ new Date();
15619
+ const current = now.getUTCHours() * 100 + now.getUTCMinutes();
15620
+ const issuances = [100, 600, 1300, 1630, 2e3];
15621
+ let latest = issuances[0];
15622
+ for (const issuance of issuances) {
15623
+ if (current >= issuance) {
15624
+ latest = issuance;
15625
+ }
15626
+ }
15627
+ return latest.toString().padStart(4, "0");
15628
+ };
15629
+
15630
+ // src/@building/building.outlooks.ts
15631
+ var getEventAttachments = (event) => {
15632
+ var _a, _b;
15633
+ const latestTime = getLatestIssuance();
15634
+ const validEvents = [
15635
+ { target: "Day 1", attachment: `https://www.spc.noaa.gov/products/outlook/day1otlk_${latestTime}.png` },
15636
+ { target: "Day 2", attachment: `https://www.spc.noaa.gov/products/outlook/day2otlk.png` },
15637
+ { target: "Day 3", attachment: `https://www.spc.noaa.gov/products/outlook/day3otlk.png` },
15638
+ { target: "Mesoscale Discussion", attachment: `https://www.spc.noaa.gov/products/md/mcd${(_b = (_a = event == null ? void 0 : event.properties) == null ? void 0 : _a.spc_parameters) == null ? void 0 : _b.spc_number}.png` }
15639
+ ];
15640
+ const isValid = validEvents.find((outlook) => outlook.target == event.properties.event);
15641
+ if (isValid) {
15642
+ return (isValid == null ? void 0 : isValid.attachment) ? [isValid.attachment] : [];
15643
+ }
15644
+ return null;
15645
+ };
15646
+
15158
15647
  // src/@building/building.validate.ts
15159
15648
  var validateEvents = (events) => __async(null, null, function* () {
15160
15649
  var _a;
@@ -15185,6 +15674,7 @@ var validateEvents = (events) => __async(null, null, function* () {
15185
15674
  }
15186
15675
  filteredProperties.metadata = (_a2 = filteredProperties.metadata) != null ? _a2 : {};
15187
15676
  properties2.metadata.hash = createHash("sha256").update(JSON.stringify(filteredProperties)).digest("hex");
15677
+ properties2.metadata.attachments = getEventAttachments(event);
15188
15678
  setEventEmit({ event: `onProductType${enhancedEventName.replace(/\s+/g, "")}`, metadata: define2 });
15189
15679
  if (properties2.status_metadata.is_test) {
15190
15680
  setEventEmit({ event: `onTestProduct`, metadata: define2 });
@@ -15309,6 +15799,7 @@ var text = (stanza) => __async(null, null, function* () {
15309
15799
  header,
15310
15800
  vtec: null,
15311
15801
  hvtec: null,
15802
+ raw: message,
15312
15803
  history: [
15313
15804
  {
15314
15805
  description: props.description,
@@ -15445,6 +15936,7 @@ var ugc = (stanza) => __async(null, null, function* () {
15445
15936
  header,
15446
15937
  vtec: null,
15447
15938
  hvtec: null,
15939
+ raw: message,
15448
15940
  history: [
15449
15941
  {
15450
15942
  description: props.description,
@@ -15677,6 +16169,7 @@ var vtec = (stanza) => __async(null, null, function* () {
15677
16169
  header,
15678
16170
  vtec: pv,
15679
16171
  hvtec: hVtec,
16172
+ raw: message,
15680
16173
  history: [
15681
16174
  {
15682
16175
  description: props.description,
@@ -15695,7 +16188,7 @@ var vtec = (stanza) => __async(null, null, function* () {
15695
16188
 
15696
16189
  // src/@events/events.api.ts
15697
16190
  var api = (stanza) => __async(null, null, function* () {
15698
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb;
16191
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb;
15699
16192
  let processed = [];
15700
16193
  const messages = Object.values(JSON.parse(stanza.message).features);
15701
16194
  for (const feature of messages) {
@@ -15741,37 +16234,40 @@ var api = (stanza) => __async(null, null, function* () {
15741
16234
  hail_threat: (_ma = (_la = (_ka = (_ja = feature == null ? void 0 : feature.properties) == null ? void 0 : _ja.parameters) == null ? void 0 : _ka.hailThreat) == null ? void 0 : _la[0]) != null ? _ma : null
15742
16235
  },
15743
16236
  spc_parameters: {
15744
- spc_max_tornado: (_oa = getTextFromProduct({ message: (_na = feature == null ? void 0 : feature.properties) == null ? void 0 : _na.description, find: [`MOST PROBABLE PEAK TORNADO INTENSITY...`] })) != null ? _oa : null,
15745
- spc_max_hail: (_qa = getTextFromProduct({ message: (_pa = feature == null ? void 0 : feature.properties) == null ? void 0 : _pa.description, find: [`MOST PROBABLE PEAK HAIL SIZE...`] })) != null ? _qa : null,
15746
- spc_max_wind: (_sa = getTextFromProduct({ message: (_ra = feature == null ? void 0 : feature.properties) == null ? void 0 : _ra.description, find: [`MOST PROBABLE PEAK WIND GUST...`] })) != null ? _sa : null,
15747
- spc_watch_issuance: (_ua = getTextFromProduct({ message: (_ta = feature == null ? void 0 : feature.properties) == null ? void 0 : _ta.description, find: [`Probability of Watch Issuance...`], removal: [`percent`] })) != null ? _ua : null
16237
+ spc_number: (_oa = getTextFromProduct({ message: (_na = feature == null ? void 0 : feature.properties) == null ? void 0 : _na.description, find: [`Mesoscale Discussion `], removal: [`Mesoscale Discussion`, `Number`, `...`] })) != null ? _oa : null,
16238
+ spc_concerning: (_qa = getTextFromProduct({ message: (_pa = feature == null ? void 0 : feature.properties) == null ? void 0 : _pa.description, find: [`Concerning...`] })) != null ? _qa : null,
16239
+ spc_max_tornado: (_sa = getTextFromProduct({ message: (_ra = feature == null ? void 0 : feature.properties) == null ? void 0 : _ra.description, find: [`MOST PROBABLE PEAK TORNADO INTENSITY...`] })) != null ? _sa : null,
16240
+ spc_max_hail: (_ua = getTextFromProduct({ message: (_ta = feature == null ? void 0 : feature.properties) == null ? void 0 : _ta.description, find: [`MOST PROBABLE PEAK HAIL SIZE...`] })) != null ? _ua : null,
16241
+ spc_max_wind: (_wa = getTextFromProduct({ message: (_va = feature == null ? void 0 : feature.properties) == null ? void 0 : _va.description, find: [`MOST PROBABLE PEAK WIND GUST...`] })) != null ? _wa : null,
16242
+ spc_watch_issuance: (_ya = getTextFromProduct({ message: (_xa = feature == null ? void 0 : feature.properties) == null ? void 0 : _xa.description, find: [`Probability of Watch Issuance...`], removal: [`percent`] })) != null ? _ya : null
15748
16243
  },
15749
16244
  watch_parameters: {
15750
- watch_number: (_ya = (_xa = (_wa = getTextFromProduct({ message: (_va = feature == null ? void 0 : feature.properties) == null ? void 0 : _va.description, find: [`ITIES FOR`, `UPDATE FOR`, `Watch Number `], removal: [`%`, `<`, `:`] })) == null ? void 0 : _wa.replace(/(WT|WS|)/g, "")) == null ? void 0 : _xa.trim()) != null ? _ya : null,
15751
- watch_type: ((_za = feature == null ? void 0 : feature.properties) == null ? void 0 : _za.description.includes(`TORNADO WATCH`)) ? `Tornado` : ((_Aa = feature == null ? void 0 : feature.properties) == null ? void 0 : _Aa.description.includes(`SEVERE`)) ? `Severe` : null,
15752
- additional_tornadoes_probability: (_Ca = getTextFromProduct({ message: (_Ba = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ba.description, find: [`PROB OF 2 OR MORE TORNADOES`], removal: [`%`, `<`, `:`] })) != null ? _Ca : null,
15753
- strong_tornadoes_probability: (_Ea = getTextFromProduct({ message: (_Da = feature == null ? void 0 : feature.properties) == null ? void 0 : _Da.description, find: [`PROB OF 1 OR MORE STRONG /EF2-EF5/ TORNADOES`], removal: [`%`, `<`, `:`] })) != null ? _Ea : null,
15754
- severe_wind_probability: (_Ga = getTextFromProduct({ message: (_Fa = feature == null ? void 0 : feature.properties) == null ? void 0 : _Fa.description, find: [`PROB OF 10 OR MORE SEVERE WIND EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _Ga : null,
15755
- severe_hail_probability: (_Ia = getTextFromProduct({ message: (_Ha = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ha.description, find: [`PROB OF 10 OR MORE SEVERE HAIL EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _Ia : null,
15756
- hail_2in_probability: (_Ka = getTextFromProduct({ message: (_Ja = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ja.description, find: [`PROB OF 1 OR MORE HAIL EVENTS >= 2 INCHES`], removal: [`%`, `<`, `:`] })) != null ? _Ka : null,
15757
- combined_hail_wind_probability: (_Ma = getTextFromProduct({ message: (_La = feature == null ? void 0 : feature.properties) == null ? void 0 : _La.description, find: [`PROB OF 6 OR MORE COMBINED SEVERE HAIL/WIND EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _Ma : null,
15758
- max_hail_in: (_Oa = getTextFromProduct({ message: (_Na = feature == null ? void 0 : feature.properties) == null ? void 0 : _Na.description, find: [`MAX HAIL /INCHES/`], removal: [`%`, `<`, `:`] })) != null ? _Oa : null,
15759
- max_wind_surface: (_Qa = getTextFromProduct({ message: (_Pa = feature == null ? void 0 : feature.properties) == null ? void 0 : _Pa.description, find: [`MAX WIND GUSTS SURFACE /KNOTS/`], removal: [`%`, `<`, `:`] })) != null ? _Qa : null,
15760
- max_tops_x100feet: (_Sa = getTextFromProduct({ message: (_Ra = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ra.description, find: [`MAX TOPS /X 100 FEET/`], removal: [`%`, `<`, `:`] })) != null ? _Sa : null,
15761
- pds_watch: getTextFromProduct({ message: (_Ta = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ta.description, find: [`PARTICULARLY DANGEROUS SITUATION`], removal: [`%`, `<`, `:`] }) === `YES`
16245
+ watch_number: (_Ca = (_Ba = (_Aa = getTextFromProduct({ message: (_za = feature == null ? void 0 : feature.properties) == null ? void 0 : _za.description, find: [`ITIES FOR`, `UPDATE FOR`, `Watch Number `], removal: [`%`, `<`, `:`] })) == null ? void 0 : _Aa.replace(/(WT|WS|)/g, "")) == null ? void 0 : _Ba.trim()) != null ? _Ca : null,
16246
+ watch_type: ((_Da = feature == null ? void 0 : feature.properties) == null ? void 0 : _Da.description.includes(`TORNADO WATCH`)) ? `Tornado` : ((_Ea = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ea.description.includes(`SEVERE`)) ? `Severe` : null,
16247
+ additional_tornadoes_probability: (_Ga = getTextFromProduct({ message: (_Fa = feature == null ? void 0 : feature.properties) == null ? void 0 : _Fa.description, find: [`PROB OF 2 OR MORE TORNADOES`], removal: [`%`, `<`, `:`] })) != null ? _Ga : null,
16248
+ strong_tornadoes_probability: (_Ia = getTextFromProduct({ message: (_Ha = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ha.description, find: [`PROB OF 1 OR MORE STRONG /EF2-EF5/ TORNADOES`], removal: [`%`, `<`, `:`] })) != null ? _Ia : null,
16249
+ severe_wind_probability: (_Ka = getTextFromProduct({ message: (_Ja = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ja.description, find: [`PROB OF 10 OR MORE SEVERE WIND EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _Ka : null,
16250
+ severe_hail_probability: (_Ma = getTextFromProduct({ message: (_La = feature == null ? void 0 : feature.properties) == null ? void 0 : _La.description, find: [`PROB OF 10 OR MORE SEVERE HAIL EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _Ma : null,
16251
+ hail_2in_probability: (_Oa = getTextFromProduct({ message: (_Na = feature == null ? void 0 : feature.properties) == null ? void 0 : _Na.description, find: [`PROB OF 1 OR MORE HAIL EVENTS >= 2 INCHES`], removal: [`%`, `<`, `:`] })) != null ? _Oa : null,
16252
+ combined_hail_wind_probability: (_Qa = getTextFromProduct({ message: (_Pa = feature == null ? void 0 : feature.properties) == null ? void 0 : _Pa.description, find: [`PROB OF 6 OR MORE COMBINED SEVERE HAIL/WIND EVENTS`], removal: [`%`, `<`, `:`] })) != null ? _Qa : null,
16253
+ max_hail_in: (_Sa = getTextFromProduct({ message: (_Ra = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ra.description, find: [`MAX HAIL /INCHES/`], removal: [`%`, `<`, `:`] })) != null ? _Sa : null,
16254
+ max_wind_surface: (_Ua = getTextFromProduct({ message: (_Ta = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ta.description, find: [`MAX WIND GUSTS SURFACE /KNOTS/`], removal: [`%`, `<`, `:`] })) != null ? _Ua : null,
16255
+ max_tops_x100feet: (_Wa = getTextFromProduct({ message: (_Va = feature == null ? void 0 : feature.properties) == null ? void 0 : _Va.description, find: [`MAX TOPS /X 100 FEET/`], removal: [`%`, `<`, `:`] })) != null ? _Wa : null,
16256
+ pds_watch: getTextFromProduct({ message: (_Xa = feature == null ? void 0 : feature.properties) == null ? void 0 : _Xa.description, find: [`PARTICULARLY DANGEROUS SITUATION`], removal: [`%`, `<`, `:`] }) === `YES`
15762
16257
  },
15763
16258
  metadata: {
15764
16259
  ms: performance.now() - tick,
15765
16260
  source: `events.api`,
15766
- tracking: getEventTracking({ type: `API`, organization: { wmoidentifier: (_Wa = (_Va = (_Ua = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ua.parameters) == null ? void 0 : _Va.WMOidentifier) == null ? void 0 : _Wa[0], featureId: feature == null ? void 0 : feature.id }, vtec: pVtec }),
15767
- header: `ZCZC-ATMOSX-${(_Ya = (_Xa = feature == null ? void 0 : feature.properties) == null ? void 0 : _Xa.parameters) == null ? void 0 : _Ya.WMOidentifier}`,
16261
+ tracking: getEventTracking({ type: `API`, organization: { wmoidentifier: (__a = (_Za = (_Ya = feature == null ? void 0 : feature.properties) == null ? void 0 : _Ya.parameters) == null ? void 0 : _Za.WMOidentifier) == null ? void 0 : __a[0], featureId: feature == null ? void 0 : feature.id }, vtec: pVtec }),
16262
+ header: `ZCZC-ATMOSX-${(_ab = (_$a = feature == null ? void 0 : feature.properties) == null ? void 0 : _$a.parameters) == null ? void 0 : _ab.WMOidentifier}`,
15768
16263
  vtec: pVtec,
15769
16264
  hvtec: null,
16265
+ raw: (_bb = feature == null ? void 0 : feature.properties) == null ? void 0 : _bb.description,
15770
16266
  history: [
15771
16267
  {
15772
- description: (_Za = feature == null ? void 0 : feature.properties) == null ? void 0 : _Za.description,
15773
- issued: ((__a = feature == null ? void 0 : feature.properties) == null ? void 0 : __a.sent) ? new Date((_$a = feature == null ? void 0 : feature.properties) == null ? void 0 : _$a.sent).toISOString() : null,
15774
- status: (_bb = (_ab = feature == null ? void 0 : feature.properties) == null ? void 0 : _ab.messageType) != null ? _bb : null
16268
+ description: (_cb = feature == null ? void 0 : feature.properties) == null ? void 0 : _cb.description,
16269
+ issued: ((_db = feature == null ? void 0 : feature.properties) == null ? void 0 : _db.sent) ? new Date((_eb = feature == null ? void 0 : feature.properties) == null ? void 0 : _eb.sent).toISOString() : null,
16270
+ status: (_gb = (_fb = feature == null ? void 0 : feature.properties) == null ? void 0 : _fb.messageType) != null ? _gb : null
15775
16271
  }
15776
16272
  ]
15777
16273
  }
@@ -15959,613 +16455,250 @@ var importShapefiles = () => __async(null, null, function* () {
15959
16455
  if (properties2.FIPS) {
15960
16456
  final2 = `${properties2.STATE}${shapefile.id}${properties2.FIPS.substring(2)}`;
15961
16457
  location = `${properties2.COUNTYNAME}, ${properties2.STATE}`;
15962
- } else if (properties2.FULLSTAID) {
15963
- final2 = `${properties2.ST}${shapefile.id}${properties2.WFO}`;
15964
- location = `${properties2.CITY}, ${properties2.STATE}`;
15965
- } else if (properties2.STATE) {
15966
- final2 = `${properties2.STATE}${shapefile.id}${(_a2 = properties2.ZONE) != null ? _a2 : properties2.SITE_ID}`;
15967
- location = `${(_b2 = properties2.NAME) != null ? _b2 : `${properties2.RFC_NAME} ${properties2.RFC_CITY}`}, ${properties2.STATE}`;
15968
- } else {
15969
- final2 = (_c = properties2.ID) != null ? _c : properties2.WFO;
15970
- location = properties2.NAME;
15971
- }
15972
- insert.run(final2, location, JSON.stringify(geometry));
15973
- }
15974
- });
15975
- fs.unlinkSync(`${filepath}.shp`);
15976
- fs.unlinkSync(`${filepath}.dbf`);
15977
- setWarning({ message: `Cleaned up temporary files for ${shapefile.name}_${shapefile.id}` });
15978
- transaction(features);
15979
- }
15980
- setWarning({ message: `Shapefiles have finished compiling, you can now continue and close this terminal` });
15981
- fs.rm(resolve5(__dirname, "../../shapefiles"), { recursive: true, force: true }, () => {
15982
- });
15983
- }
15984
- } catch (error) {
15985
- setWarning({ message: `An error occurred while compiling shapefiles: ${error.message}` });
15986
- }
15987
- });
15988
-
15989
- // src/@modules/@database/database.init.ts
15990
- var initializeDatabase = () => __async(null, null, function* () {
15991
- const settings = bootstrap.settings;
15992
- try {
15993
- if (!fs2.existsSync(settings.Database)) {
15994
- fs2.writeFileSync(settings.Database, "");
15995
- setWarning({ message: `Creating new database at ${settings.Database}` });
15996
- }
15997
- bootstrap.database = new sqlite3(settings.Database);
15998
- bootstrap.database.prepare(`CREATE TABLE IF NOT EXISTS stanzas ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT, issued TEXT, stanza TEXT )`).run();
15999
- bootstrap.database.prepare(`CREATE TABLE IF NOT EXISTS shapefiles (id TEXT PRIMARY KEY, location TEXT, geometry TEXT)`).run();
16000
- yield importShapefiles();
16001
- } catch (error) {
16002
- setWarning({ message: `An error occurred while initializing the database: ${error.message}` });
16003
- }
16004
- });
16005
-
16006
- // src/@modules/@database/database.cache.ts
16007
- var getCachedEvents = () => __async(null, null, function* () {
16008
- var _a;
16009
- try {
16010
- const settings = bootstrap.settings;
16011
- const tick = performance.now();
16012
- if (settings.NOAAWeatherWireServiceSettings.CacheSettings.Enabled) {
16013
- const max = (_a = settings.NOAAWeatherWireServiceSettings.CacheSettings.MaxRetentionHistory) != null ? _a : 500;
16014
- const get = yield bootstrap.database.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`).all(max);
16015
- setWarning({ message: `Fetched ${get.length} cached events from the database in ${Math.floor(performance.now() - tick)} ms` });
16016
- let events = get.map((row) => JSON.parse(row.stanza)).filter((stanza) => {
16017
- if (!stanza) {
16018
- return;
16019
- }
16020
- const isSkippable = stanza.isIgnored || stanza.isCapEvent || stanza.isCapEvent && !stanza.isCapAreaDescription;
16021
- return !isSkippable;
16022
- });
16023
- events = events.sort((a, b) => b.issued - a.issued);
16024
- yield Promise.all(events.map((event) => createEvent(event)));
16025
- setWarning({ message: `Processed ${events.length} cached events in ${Math.floor(performance.now() - tick)} ms` });
16026
- }
16027
- } catch (error) {
16028
- setWarning({ message: `An error occurred while fetching cached events: ${error.message} -> ${error.stack}` });
16029
- }
16030
- });
16031
-
16032
- // src/@modules/@xmpp/xmpp.xReconnect.ts
16033
- var xReconnect = (interval) => __async(null, null, function* () {
16034
- const settings = bootstrap.settings;
16035
- const lastStanza = Date.now() - bootstrap.cache.lastStanza;
16036
- if (interval < 15) {
16037
- setWarning({ message: `Reconnection interval of ${interval} seconds is too low, setting to 15 seconds` });
16038
- interval = 15;
16039
- bootstrap.settings.NOAAWeatherWireServiceSettings.ReconnectionSettings.ReconnectionInterval = 15;
16040
- }
16041
- const reconnectThreshold = interval * 1e3;
16042
- if (!bootstrap.cache.isConnected && !bootstrap.cache.sigHault || !bootstrap.session_xmpp) {
16043
- return;
16044
- }
16045
- if (lastStanza > reconnectThreshold) {
16046
- if (!bootstrap.cache.isReconnecting) {
16047
- bootstrap.cache.isReconnecting = true;
16048
- bootstrap.cache.isConnected = false;
16049
- bootstrap.cache.tReconnects += 1;
16050
- try {
16051
- setEventEmit({
16052
- event: `onServiceStatus`,
16053
- metadata: {
16054
- message: `Attempting to reconnect to XMPP Service (Reconnect Attempt ${bootstrap.cache.tReconnects})`,
16055
- data: {
16056
- last_stanza: lastStanza,
16057
- nickname: settings.NOAAWeatherWireServiceSettings.CredentialSettings.Nickname
16058
- },
16059
- type: `reconnect`,
16060
- error: true
16061
- }
16062
- });
16063
- yield bootstrap.session_xmpp.stop().catch(() => {
16064
- });
16065
- yield bootstrap.session_xmpp.start().catch(() => {
16066
- });
16067
- } catch (error) {
16068
- setWarning({ message: `XMPP Reconnect Failed - ${error.message}` });
16069
- } finally {
16070
- bootstrap.cache.isReconnecting = false;
16071
- }
16072
- }
16073
- }
16074
- });
16075
-
16076
- // src/@modules/@utilities/utilities.setCronSchedule.ts
16077
- var setCronSchedule = () => __async(null, null, function* () {
16078
- const settings = bootstrap.settings;
16079
- if (settings.EnableWireService) {
16080
- if (settings.NOAAWeatherWireServiceSettings.ReconnectionSettings.Enabled) {
16081
- void xReconnect(settings.NOAAWeatherWireServiceSettings.ReconnectionSettings.ReconnectionInterval);
16082
- }
16083
- } else {
16084
- const response2 = yield createHttp({
16085
- url: settings.NationalWeatherServiceSettings.EventsEndpoint,
16086
- headers: {
16087
- "User-Agent": "@atmosx/event-product-parser"
16088
- }
16089
- });
16090
- if (response2.error) {
16091
- return setEventEmit({
16092
- event: `onServiceStatus`,
16093
- metadata: {
16094
- type: "fetch-api",
16095
- message: `Failed to fetch latest events from National Weather Service API - ${response2.message}`,
16096
- data: {},
16097
- error: true
16098
- }
16099
- });
16100
- }
16101
- setEventEmit({
16102
- event: `onServiceStatus`,
16103
- metadata: {
16104
- message: `Fetched latest events from National Weather Service API`,
16105
- data: {},
16106
- type: "fetch-api",
16107
- error: false
16108
- }
16109
- });
16110
- createEvent({ message: response2.message, isNWWS: false });
16111
- }
16112
- });
16113
-
16114
- // src/@manager/manager.updateEvents.ts
16115
- var updateEvents = (selectedEvent) => __async(null, null, function* () {
16116
- const events = bootstrap.cache.events.features;
16117
- function update(evt) {
16118
- return __async(this, null, function* () {
16119
- if (new Date(evt.properties.expires) < /* @__PURE__ */ new Date()) {
16120
- rmEvent(evt);
16121
- }
16122
- });
16123
- }
16124
- if (!selectedEvent) {
16125
- yield Promise.all(events.map((evt) => __async(null, null, function* () {
16126
- yield update(evt);
16127
- })));
16128
- }
16129
- if (selectedEvent) {
16130
- yield update(selectedEvent);
16131
- }
16132
- });
16133
-
16134
- // src/@core/core.start.ts
16135
- import { Cron } from "croner";
16136
- var startService = (configurations) => __async(null, null, function* () {
16137
- if (!bootstrap.isReady) {
16138
- return setWarning({
16139
- message: `You can not create another instance without shutting down the current one first, please make sure to call the stop() method first!`
16140
- });
16141
- }
16142
- const settings = setSettings(configurations);
16143
- bootstrap.isReady = true;
16144
- yield initializeDatabase();
16145
- if (settings.EnableWireService) {
16146
- (() => __async(null, null, function* () {
16147
- yield getCachedEvents();
16148
- yield xDeploy();
16149
- }))();
16150
- }
16151
- yield setCronSchedule();
16152
- let scheduleInterval = !settings.EnableWireService ? settings.NationalWeatherServiceSettings.CallbackInterval : 1;
16153
- if (!settings.EnableWireService && scheduleInterval < 15) {
16154
- setWarning({ message: `Schedule interval of ${scheduleInterval} seconds is too low, setting to 15 seconds` });
16155
- bootstrap.settings.NationalWeatherServiceSettings.CallbackInterval = 15;
16156
- scheduleInterval = 15;
16157
- }
16158
- bootstrap.cron = new Cron(`*/${scheduleInterval} * * * * *`, () => __async(null, null, function* () {
16159
- yield setCronSchedule();
16160
- }));
16161
- bootstrap.cron = new Cron(`* * * * * *`, () => __async(null, null, function* () {
16162
- yield updateNode();
16163
- yield updateEvents();
16164
- }));
16165
- });
16166
-
16167
- // src/@core/core.stop.ts
16168
- var stopService = () => __async(null, null, function* () {
16169
- if (bootstrap.isReady) {
16170
- bootstrap.isReady = false;
16171
- if (bootstrap.session_xmpp) {
16172
- try {
16173
- yield bootstrap.session_xmpp.stop();
16174
- } catch (e) {
16458
+ } else if (properties2.FULLSTAID) {
16459
+ final2 = `${properties2.ST}${shapefile.id}${properties2.WFO}`;
16460
+ location = `${properties2.CITY}, ${properties2.STATE}`;
16461
+ } else if (properties2.STATE) {
16462
+ final2 = `${properties2.STATE}${shapefile.id}${(_a2 = properties2.ZONE) != null ? _a2 : properties2.SITE_ID}`;
16463
+ location = `${(_b2 = properties2.NAME) != null ? _b2 : `${properties2.RFC_NAME} ${properties2.RFC_CITY}`}, ${properties2.STATE}`;
16464
+ } else {
16465
+ final2 = (_c = properties2.ID) != null ? _c : properties2.WFO;
16466
+ location = properties2.NAME;
16467
+ }
16468
+ insert.run(final2, location, JSON.stringify(geometry));
16469
+ }
16470
+ });
16471
+ fs.unlinkSync(`${filepath}.shp`);
16472
+ fs.unlinkSync(`${filepath}.dbf`);
16473
+ setWarning({ message: `Cleaned up temporary files for ${shapefile.name}_${shapefile.id}` });
16474
+ transaction(features);
16175
16475
  }
16176
- bootstrap.cache.isConnected = false;
16177
- bootstrap.cache.sigHault = true;
16178
- bootstrap.session_xmpp = null;
16476
+ setWarning({ message: `Shapefiles have finished compiling, you can now continue and close this terminal` });
16477
+ fs.rm(resolve5(__dirname, "../../shapefiles"), { recursive: true, force: true }, () => {
16478
+ });
16179
16479
  }
16480
+ } catch (error) {
16481
+ setWarning({ message: `An error occurred while compiling shapefiles: ${error.message}` });
16180
16482
  }
16181
16483
  });
16182
16484
 
16183
- // src/@dictionaries/dictionaries.transcribedMessageReplacements.ts
16184
- var transcribedMessageReplacements = [
16185
- { regex: /\*/g, replacement: "" },
16186
- { regex: /\.{3,}/g, replacement: "" },
16187
- { regex: /\bUTC\b/g, replacement: "Coordinated Universal Time" },
16188
- { regex: /\bGMT\b/g, replacement: "Greenwich Mean Time" },
16189
- { regex: /\bEST\b(?!\w)/g, replacement: "Eastern Standard Time" },
16190
- { regex: /\bEDT\b(?!\w)/g, replacement: "Eastern Daylight Time" },
16191
- { regex: /\bCST\b(?!\w)/g, replacement: "Central Standard Time" },
16192
- { regex: /\bCDT\b(?!\w)/g, replacement: "Central Daylight Time" },
16193
- { regex: /\bMST\b(?!\w)/g, replacement: "Mountain Standard Time" },
16194
- { regex: /\bMDT\b(?!\w)/g, replacement: "Mountain Daylight Time" },
16195
- { regex: /\bPST\b(?!\w)/g, replacement: "Pacific Standard Time" },
16196
- { regex: /\bPDT\b(?!\w)/g, replacement: "Pacific Daylight Time" },
16197
- { regex: /\bAKST\b(?!\w)/g, replacement: "Alaska Standard Time" },
16198
- { regex: /\bAKDT\b(?!\w)/g, replacement: "Alaska Daylight Time" },
16199
- { regex: /\bHST\b(?!\w)/g, replacement: "Hawaii Standard Time" },
16200
- { regex: /\bHDT\b(?!\w)/g, replacement: "Hawaii Daylight Time" },
16201
- { regex: /\bmph\b(?!\w)/g, replacement: "miles per hour" },
16202
- { regex: /\bkm\/h\b(?!\w)/g, replacement: "kilometers per hour" },
16203
- { regex: /\bkmh\b(?!\w)/g, replacement: "kilometers per hour" },
16204
- { regex: /\bkt\b(?!\w)/g, replacement: "knots" },
16205
- { regex: /\bNE\b(?!\w)/g, replacement: "northeast" },
16206
- { regex: /\bNW\b(?!\w)/g, replacement: "northwest" },
16207
- { regex: /\bSE\b(?!\w)/g, replacement: "southeast" },
16208
- { regex: /\bSW\b(?!\w)/g, replacement: "southwest" },
16209
- { regex: /\bNM\b(?!\w)/g, replacement: "nautical miles" },
16210
- { regex: /\bdeg\b(?!\w)/g, replacement: "degrees" },
16211
- { regex: /\btstm\b(?!\w)/g, replacement: "thunderstorm" },
16212
- { regex: /\bmm\b(?!\w)/g, replacement: "millimeters" },
16213
- { regex: /\bcm\b(?!\w)/g, replacement: "centimeters" },
16214
- { regex: /\bin.\b(?!\w)/g, replacement: "inches" },
16215
- { regex: /\bft\b(?!\w)/g, replacement: "feet" },
16216
- { regex: /\bmi\b(?!\w)/g, replacement: "miles" },
16217
- { regex: /\bhr\b(?!\w)/g, replacement: "hour" },
16218
- { regex: /\bhourly\b(?!\w)/g, replacement: "per hour" },
16219
- { regex: /\bkg\b(?!\w)/g, replacement: "kilograms" },
16220
- { regex: /\bg\/kg\b(?!\w)/g, replacement: "grams per kilogram" },
16221
- { regex: /\bmb\b(?!\w)/g, replacement: "millibars" },
16222
- { regex: /\bhPa\b(?!\w)/g, replacement: "hectopascals" },
16223
- { regex: /\bPa\b(?!\w)/g, replacement: "pascals" },
16224
- { regex: /\bKPa\b(?!\w)/g, replacement: "kilopascals" },
16225
- { regex: /\bC\/hr\b(?!\w)/g, replacement: "degrees Celsius per hour" },
16226
- { regex: /\bF\/hr\b(?!\w)/g, replacement: "degrees Fahrenheit per hour" },
16227
- { regex: /\bC\/min\b(?!\w)/g, replacement: "degrees Celsius per minute" },
16228
- { regex: /\bF\/min\b(?!\w)/g, replacement: "degrees Fahrenheit per minute" },
16229
- { regex: /\bC\b(?!\w)/g, replacement: "degrees Celsius" },
16230
- { regex: /\bF\b(?!\w)/g, replacement: "degrees Fahrenheit" }
16231
- ];
16232
-
16233
- // src/@modules/@eas/eas.getWavPCM16.ts
16234
- var getWavPCM16 = (buffer) => {
16235
- if (buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WAVE") {
16236
- return null;
16237
- }
16238
- let fmt = null;
16239
- let data = null;
16240
- let i = 12;
16241
- while (i + 8 <= buffer.length) {
16242
- const id2 = buffer.toString("ascii", i, i + 4);
16243
- const size = buffer.readUInt32LE(i + 4);
16244
- const start = i + 8;
16245
- const end = start + size;
16246
- if (id2 === "fmt ") fmt = buffer.slice(start, end);
16247
- if (id2 === "data") data = buffer.slice(start, end);
16248
- i = end + size % 2;
16249
- }
16250
- if (!fmt || !data) return null;
16251
- const audioFormat = fmt.readUInt16LE(0);
16252
- const channels = fmt.readUInt16LE(2);
16253
- const sampleRate = fmt.readUInt32LE(4);
16254
- const bitsPerSample = fmt.readUInt16LE(14);
16255
- if (audioFormat !== 1 || bitsPerSample !== 16 || channels !== 1) {
16256
- return null;
16257
- }
16258
- const samples = new Int16Array(data.buffer, data.byteOffset, data.length / 2);
16259
- return { samples: new Int16Array(samples), sampleRate, channels, bitsPerSample };
16260
- };
16261
-
16262
- // src/@modules/@eas/eas.getSampledPCM16.ts
16263
- var getSampledPCM16 = (int16, originalRate, targetRate) => {
16264
- if (originalRate === targetRate) return int16;
16265
- const ratio = targetRate / originalRate;
16266
- const outLen = Math.max(1, Math.round(int16.length * ratio));
16267
- const out = new Int16Array(outLen);
16268
- for (let i = 0; i < outLen; i++) {
16269
- const pos = i / ratio;
16270
- const i0 = Math.floor(pos);
16271
- const i1 = Math.min(i0 + 1, int16.length - 1);
16272
- const frac = pos - i0;
16273
- const v = int16[i0] * (1 - frac) + int16[i1] * frac;
16274
- out[i] = Math.round(v);
16275
- }
16276
- return out;
16277
- };
16278
-
16279
- // src/@modules/@eas/eas.getPCMToFloat.ts
16280
- var getPCMToFloat = (int16) => {
16281
- const out = new Float32Array(int16.length);
16282
- for (let i = 0; i < int16.length; i++) out[i] = int16[i] / 32768;
16283
- return out;
16284
- };
16285
-
16286
- // src/@modules/@eas/eas.getFloatPCM16.ts
16287
- var getFloatPCM16 = (float32) => {
16288
- const out = new Int16Array(float32.length);
16289
- for (let i = 0; i < float32.length; i++) {
16290
- let v = Math.max(-1, Math.min(1, float32[i]));
16291
- out[i] = Math.round(v * 32767);
16292
- }
16293
- return out;
16294
- };
16295
-
16296
- // src/@modules/@eas/eas.setRadioEffect.ts
16297
- var setRadioEffect = (int16, sampleRate) => {
16298
- const hpCut = 3555;
16299
- const lpCut = 1600;
16300
- const x = getPCMToFloat(int16);
16301
- const dt = 1 / sampleRate;
16302
- const rcHP = 1 / (2 * Math.PI * hpCut);
16303
- const aHP = rcHP / (rcHP + dt);
16304
- let yHP = 0, xPrev = 0;
16305
- for (let i = 0; i < x.length; i++) {
16306
- const xi = x[i];
16307
- yHP = aHP * (yHP + xi - xPrev);
16308
- xPrev = xi;
16309
- x[i] = yHP;
16310
- }
16311
- const rcLP = 1 / (2 * Math.PI * lpCut);
16312
- const aLP = dt / (rcLP + dt);
16313
- let yLP = 0;
16314
- for (let i = 0; i < x.length; i++) {
16315
- yLP = yLP + aLP * (x[i] - yLP);
16316
- x[i] = yLP;
16485
+ // src/@modules/@database/database.init.ts
16486
+ var initializeDatabase = () => __async(null, null, function* () {
16487
+ const settings = bootstrap.settings;
16488
+ try {
16489
+ if (!fs2.existsSync(settings.Database)) {
16490
+ fs2.writeFileSync(settings.Database, "");
16491
+ setWarning({ message: `Creating new database at ${settings.Database}` });
16492
+ }
16493
+ bootstrap.database = new sqlite3(settings.Database);
16494
+ bootstrap.database.prepare(`CREATE TABLE IF NOT EXISTS stanzas ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT, issued TEXT, stanza TEXT )`).run();
16495
+ bootstrap.database.prepare(`CREATE TABLE IF NOT EXISTS shapefiles (id TEXT PRIMARY KEY, location TEXT, geometry TEXT)`).run();
16496
+ yield importShapefiles();
16497
+ } catch (error) {
16498
+ setWarning({ message: `An error occurred while initializing the database: ${error.message}` });
16317
16499
  }
16318
- const compGain = 2;
16319
- const norm = Math.tanh(compGain);
16320
- for (let i = 0; i < x.length; i++) x[i] = Math.tanh(x[i] * compGain) / norm;
16321
- return getFloatPCM16(x);
16322
- };
16500
+ });
16323
16501
 
16324
- // src/@modules/@eas/eas.setAFSK.ts
16325
- var setAFSK = (bits, sampleRate) => {
16326
- const baud = 520.83;
16327
- const markFreq = 2083.3;
16328
- const spaceFreq = 1562.5;
16329
- const amplitude = 0.6;
16330
- const twoPi = Math.PI * 2;
16331
- const result = [];
16332
- let phase = 0;
16333
- let frac = 0;
16334
- for (let b = 0; b < bits.length; b++) {
16335
- const bit = bits[b];
16336
- const freq = bit ? markFreq : spaceFreq;
16337
- const samplesPerBit = sampleRate / baud + frac;
16338
- const n = Math.round(samplesPerBit);
16339
- frac = samplesPerBit - n;
16340
- const inc = twoPi * freq / sampleRate;
16341
- for (let i = 0; i < n; i++) {
16342
- result.push(Math.round(Math.sin(phase) * amplitude * 32767));
16343
- phase += inc;
16344
- if (phase > twoPi) phase -= twoPi;
16502
+ // src/@modules/@database/database.cache.ts
16503
+ var getCachedEvents = () => __async(null, null, function* () {
16504
+ var _a;
16505
+ try {
16506
+ const settings = bootstrap.settings;
16507
+ const tick = performance.now();
16508
+ if (settings.NOAAWeatherWireServiceSettings.CacheSettings.Enabled) {
16509
+ const max = (_a = settings.NOAAWeatherWireServiceSettings.CacheSettings.MaxRetentionHistory) != null ? _a : 500;
16510
+ const get = yield bootstrap.database.prepare(`SELECT * FROM stanzas ORDER BY rowid DESC LIMIT ?`).all(max);
16511
+ setWarning({ message: `Fetched ${get.length} cached events from the database in ${Math.floor(performance.now() - tick)} ms` });
16512
+ let events = get.map((row) => JSON.parse(row.stanza)).filter((stanza) => {
16513
+ if (!stanza) {
16514
+ return;
16515
+ }
16516
+ const isSkippable = stanza.isIgnored || stanza.isCapEvent || stanza.isCapEvent && !stanza.isCapAreaDescription;
16517
+ return !isSkippable;
16518
+ });
16519
+ events = events.sort((a, b) => b.issued - a.issued);
16520
+ yield Promise.all(events.map((event) => createEvent(event)));
16521
+ setWarning({ message: `Processed ${events.length} cached events in ${Math.floor(performance.now() - tick)} ms` });
16345
16522
  }
16523
+ } catch (error) {
16524
+ setWarning({ message: `An error occurred while fetching cached events: ${error.message} -> ${error.stack}` });
16346
16525
  }
16347
- const fadeSamples = Math.floor(sampleRate * 2e-3);
16348
- for (let i = 0; i < fadeSamples; i++) {
16349
- const gain = i / fadeSamples;
16350
- result[i] = Math.round(result[i] * gain);
16351
- result[result.length - 1 - i] = Math.round(result[result.length - 1 - i] * gain);
16352
- }
16353
- return Int16Array.from(result);
16354
- };
16526
+ });
16355
16527
 
16356
- // src/@modules/@eas/eas.setAsciiToBits.ts
16357
- var setAsciiToBits = (str) => {
16358
- const bits = [];
16359
- for (let i = 0; i < str.length; i++) {
16360
- const c = str.charCodeAt(i) & 255;
16361
- bits.push(0);
16362
- for (let b = 0; b < 8; b++) bits.push(c >> b & 1);
16363
- bits.push(1, 1);
16528
+ // src/@modules/@xmpp/xmpp.xReconnect.ts
16529
+ var xReconnect = (interval) => __async(null, null, function* () {
16530
+ const settings = bootstrap.settings;
16531
+ const lastStanza = Date.now() - bootstrap.cache.lastStanza;
16532
+ if (interval < 15) {
16533
+ setWarning({ message: `Reconnection interval of ${interval} seconds is too low, setting to 15 seconds` });
16534
+ interval = 15;
16535
+ bootstrap.settings.NOAAWeatherWireServiceSettings.ReconnectionSettings.ReconnectionInterval = 15;
16364
16536
  }
16365
- return bits;
16366
- };
16367
-
16368
- // src/@modules/@eas/eas.getMergedPCM16.ts
16369
- var getMergedPCM16 = (arrays) => {
16370
- let total = 0;
16371
- for (const a of arrays) total += a.length;
16372
- const out = new Int16Array(total);
16373
- let o = 0;
16374
- for (const a of arrays) {
16375
- out.set(a, o);
16376
- o += a.length;
16537
+ const reconnectThreshold = interval * 1e3;
16538
+ if (!bootstrap.cache.isConnected && !bootstrap.cache.sigHault || !bootstrap.session_xmpp) {
16539
+ return;
16377
16540
  }
16378
- return out;
16379
- };
16380
-
16381
- // src/@modules/@eas/eas.setSameHeader.ts
16382
- var setSameHeader = (vtec2, repeats, sampleRate = 8e3, options = {}) => {
16383
- var _a, _b;
16384
- const preMarkSec = (_a = options.preMarkSec) != null ? _a : 0.3;
16385
- const gapSec = (_b = options.gapSec) != null ? _b : 0.1;
16386
- const bursts = [];
16387
- const gap = new Int16Array(Math.floor(gapSec * sampleRate));
16388
- for (let i = 0; i < repeats; i++) {
16389
- const bodyBits = setAsciiToBits(vtec2);
16390
- const body = setAFSK(bodyBits, sampleRate);
16391
- const extendedBodyDuration = Math.round(preMarkSec * sampleRate);
16392
- const extendedBody = new Int16Array(extendedBodyDuration + gap.length);
16393
- for (let j2 = 0; j2 < extendedBodyDuration; j2++) {
16394
- extendedBody[j2] = Math.round(body[j2 % body.length] * 0.2);
16541
+ if (lastStanza > reconnectThreshold) {
16542
+ if (!bootstrap.cache.isReconnecting) {
16543
+ bootstrap.cache.isReconnecting = true;
16544
+ bootstrap.cache.isConnected = false;
16545
+ bootstrap.cache.tReconnects += 1;
16546
+ try {
16547
+ setEventEmit({
16548
+ event: `onServiceStatus`,
16549
+ metadata: {
16550
+ message: `Attempting to reconnect to XMPP Service (Reconnect Attempt ${bootstrap.cache.tReconnects})`,
16551
+ data: {
16552
+ last_stanza: lastStanza,
16553
+ nickname: settings.NOAAWeatherWireServiceSettings.CredentialSettings.Nickname
16554
+ },
16555
+ type: `reconnect`,
16556
+ error: true
16557
+ }
16558
+ });
16559
+ yield bootstrap.session_xmpp.stop().catch(() => {
16560
+ });
16561
+ yield bootstrap.session_xmpp.start().catch(() => {
16562
+ });
16563
+ } catch (error) {
16564
+ setWarning({ message: `XMPP Reconnect Failed - ${error.message}` });
16565
+ } finally {
16566
+ bootstrap.cache.isReconnecting = false;
16567
+ }
16395
16568
  }
16396
- extendedBody.set(gap, extendedBodyDuration);
16397
- bursts.push(extendedBody);
16398
- if (i !== repeats - 1) bursts.push(gap);
16399
- }
16400
- return getMergedPCM16(bursts);
16401
- };
16402
-
16403
- // src/@modules/@eas/eas.setAttentionTone.ts
16404
- var setAttentionTone = (ms, sampleRate) => {
16405
- const len = Math.floor(ms * sampleRate);
16406
- const out = new Int16Array(len);
16407
- const f1 = 853;
16408
- const f2 = 960;
16409
- const twoPi = Math.PI * 2;
16410
- const amp = 0.1;
16411
- const fadeLen = Math.floor(sampleRate * 0);
16412
- for (let i = 0; i < len; i++) {
16413
- const t = i / sampleRate;
16414
- const s = Math.sin(twoPi * f1 * t) + Math.sin(twoPi * f2 * t);
16415
- let gain = 1;
16416
- if (i < fadeLen) gain = i / fadeLen;
16417
- else if (i > len - fadeLen) gain = (len - i) / fadeLen;
16418
- const v = Math.max(-1, Math.min(1, s / 2 * amp * gain));
16419
- out[i] = Math.round(v * 32767);
16420
- }
16421
- return out;
16422
- };
16423
-
16424
- // src/@modules/@eas/eas.setNoise.ts
16425
- var setNoise = (int16, noiseLevel = 0.02) => {
16426
- const x = getPCMToFloat(int16);
16427
- for (let i = 0; i < x.length; i++) x[i] += (Math.random() * 2 - 1) * noiseLevel;
16428
- let peak = 0;
16429
- for (let i = 0; i < x.length; i++) peak = Math.max(peak, Math.abs(x[i]));
16430
- if (peak > 1) for (let i = 0; i < x.length; i++) x[i] *= 0.98 / peak;
16431
- return getFloatPCM16(x);
16432
- };
16433
-
16434
- // src/@modules/@eas/eas.getPCM16.ts
16435
- var getPCM16 = (samples, sampleRate) => {
16436
- let o = 0;
16437
- const bytesPerSample = 2;
16438
- const blockAlign = 1 * bytesPerSample;
16439
- const byteRate = sampleRate * blockAlign;
16440
- const subchunk2Size = samples.length * bytesPerSample;
16441
- const chunkSize = 36 + subchunk2Size;
16442
- const buffer = Buffer.alloc(44 + subchunk2Size);
16443
- buffer.write("RIFF", o);
16444
- o += 4;
16445
- buffer.writeUInt32LE(chunkSize, o);
16446
- o += 4;
16447
- buffer.write("WAVE", o);
16448
- o += 4;
16449
- buffer.write("fmt ", o);
16450
- o += 4;
16451
- buffer.writeUInt32LE(16, o);
16452
- o += 4;
16453
- buffer.writeUInt16LE(1, o);
16454
- o += 2;
16455
- buffer.writeUInt16LE(1, o);
16456
- o += 2;
16457
- buffer.writeUInt32LE(sampleRate, o);
16458
- o += 4;
16459
- buffer.writeUInt32LE(byteRate, o);
16460
- o += 4;
16461
- buffer.writeUInt16LE(blockAlign, o);
16462
- o += 2;
16463
- buffer.writeUInt16LE(16, o);
16464
- o += 2;
16465
- buffer.write("data", o);
16466
- o += 4;
16467
- buffer.writeUInt32LE(subchunk2Size, o);
16468
- o += 4;
16469
- for (let i = 0; i < samples.length; i++, o += 2) {
16470
- buffer.writeInt16LE(samples[i].value, o);
16471
16569
  }
16472
- return buffer;
16473
- };
16570
+ });
16474
16571
 
16475
- // src/@modules/@eas/eas.setEasTone.ts
16476
- import { join } from "path";
16477
- import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
16478
- import { execSync } from "child_process";
16479
- import { platform } from "os";
16480
- import say from "say";
16481
- var setEasTone = (options) => __async(null, null, function* () {
16572
+ // src/@modules/@utilities/utilities.setCronSchedule.ts
16573
+ import { readdirSync, unlinkSync as unlinkSync2, statSync, existsSync as existsSync2 } from "fs";
16574
+ import { join as join2 } from "path";
16575
+ var setCronSchedule = () => __async(null, null, function* () {
16482
16576
  const settings = bootstrap.settings;
16483
- const directory = settings.GlobalSettings.EASSettings.ArchiveDirectory;
16484
- const prefix = settings.GlobalSettings.EASSettings.IntroWavFile;
16485
- let message = options.message;
16486
- let header = options.header;
16487
- let buffTTS;
16488
- let buffRadio;
16489
- let buffFull = [];
16490
- const tmpTTS = join(directory, `/temp/${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, "")}.wav`);
16491
- const outTTS = join(directory, `/output/${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, "")}.wav`);
16492
- const vTTS = process.platform === `win32` ? `Microsoft David Desktop` : `en-US-GuyNerual`;
16493
- const vPlatform = platform();
16494
- if (!existsSync(directory)) {
16495
- mkdirSync(directory, { recursive: true });
16577
+ const TTL = settings.GlobalSettings.EASSettings.ArchiveTTL;
16578
+ const TTLCUT = Date.now() - TTL * 1e3;
16579
+ const walk = (dir) => {
16580
+ if (existsSync2(dir)) {
16581
+ const entries = readdirSync(dir, { withFileTypes: true });
16582
+ for (const entry of entries) {
16583
+ const fullPath = join2(dir, entry.name);
16584
+ if (entry.isDirectory()) {
16585
+ walk(fullPath);
16586
+ continue;
16587
+ }
16588
+ const stats = statSync(fullPath);
16589
+ if (stats.mtime.getTime() < TTLCUT) {
16590
+ try {
16591
+ unlinkSync2(fullPath);
16592
+ } catch (err) {
16593
+ console.error(`Failed to delete ${fullPath}:`, err);
16594
+ }
16595
+ }
16596
+ }
16597
+ }
16598
+ };
16599
+ walk(settings.GlobalSettings.EASSettings.ArchiveDirectory);
16600
+ if (settings.EnableWireService) {
16601
+ if (settings.NOAAWeatherWireServiceSettings.ReconnectionSettings.Enabled) {
16602
+ void xReconnect(settings.NOAAWeatherWireServiceSettings.ReconnectionSettings.ReconnectionInterval);
16603
+ }
16604
+ } else {
16605
+ const response2 = yield createHttp({
16606
+ url: settings.NationalWeatherServiceSettings.EventsEndpoint,
16607
+ headers: {
16608
+ "User-Agent": "@atmosx/event-product-parser"
16609
+ }
16610
+ });
16611
+ if (response2.error) {
16612
+ return setEventEmit({
16613
+ event: `onServiceStatus`,
16614
+ metadata: {
16615
+ type: "fetch-api",
16616
+ message: `Failed to fetch latest events from National Weather Service API - ${response2.message}`,
16617
+ data: {},
16618
+ error: true
16619
+ }
16620
+ });
16621
+ }
16622
+ setEventEmit({
16623
+ event: `onServiceStatus`,
16624
+ metadata: {
16625
+ message: `Fetched latest events from National Weather Service API`,
16626
+ data: {},
16627
+ type: "fetch-api",
16628
+ error: false
16629
+ }
16630
+ });
16631
+ createEvent({ message: response2.message, isNWWS: false });
16496
16632
  }
16497
- if (!existsSync(join(directory, `/temp`))) {
16498
- mkdirSync(join(directory, `/temp`), { recursive: true });
16633
+ });
16634
+
16635
+ // src/@manager/manager.updateEvents.ts
16636
+ var updateEvents = (selectedEvent) => __async(null, null, function* () {
16637
+ const events = bootstrap.cache.events.features;
16638
+ function update(evt) {
16639
+ return __async(this, null, function* () {
16640
+ if (new Date(evt.properties.expires) < /* @__PURE__ */ new Date()) {
16641
+ rmEvent(evt);
16642
+ }
16643
+ });
16499
16644
  }
16500
- if (!existsSync(join(directory, `/output`))) {
16501
- mkdirSync(join(directory, `/output`), { recursive: true });
16645
+ if (!selectedEvent) {
16646
+ yield Promise.all(events.map((evt) => __async(null, null, function* () {
16647
+ yield update(evt);
16648
+ })));
16502
16649
  }
16503
- for (const { regex, replacement } of transcribedMessageReplacements) {
16504
- message = message.replace(regex, replacement);
16650
+ if (selectedEvent) {
16651
+ yield update(selectedEvent);
16505
16652
  }
16506
- if (vPlatform != `win32`) {
16507
- setWarning({
16508
- title: `EAS`,
16509
- message: `Generation isn't supported with this OS. Please wait for further updates before trying again`
16653
+ });
16654
+
16655
+ // src/@core/core.start.ts
16656
+ import { Cron } from "croner";
16657
+ var startService = (configurations) => __async(null, null, function* () {
16658
+ if (!bootstrap.isReady) {
16659
+ return setWarning({
16660
+ message: `You can not create another instance without shutting down the current one first, please make sure to call the stop() method first!`
16510
16661
  });
16511
- return null;
16512
16662
  }
16513
- say.export(message, vTTS, 1, tmpTTS);
16514
- while (!existsSync(tmpTTS) || (buffTTS = readFileSync(tmpTTS)).length == 0) {
16515
- yield setSleep({ timeout: 25 });
16663
+ const settings = setSettings(configurations);
16664
+ bootstrap.isReady = true;
16665
+ yield initializeDatabase();
16666
+ if (settings.EnableWireService) {
16667
+ (() => __async(null, null, function* () {
16668
+ yield getCachedEvents();
16669
+ yield xDeploy();
16670
+ }))();
16516
16671
  }
16517
- const vWav = getWavPCM16(buffTTS);
16518
- const vSamples = getSampledPCM16(vWav.samples, vWav.sampleRate, 8e3);
16519
- const vRadio = setRadioEffect(vSamples, 8e3);
16520
- if (existsSync(prefix)) {
16521
- let tBuffer = readFileSync(prefix);
16522
- let tWav = getWavPCM16(tBuffer);
16523
- if (tWav == null) {
16672
+ yield setCronSchedule();
16673
+ let scheduleInterval = !settings.EnableWireService ? settings.NationalWeatherServiceSettings.CallbackInterval : 1;
16674
+ if (!settings.EnableWireService && scheduleInterval < 15) {
16675
+ setWarning({ message: `Schedule interval of ${scheduleInterval} seconds is too low, setting to 15 seconds` });
16676
+ bootstrap.settings.NationalWeatherServiceSettings.CallbackInterval = 15;
16677
+ scheduleInterval = 15;
16678
+ }
16679
+ bootstrap.cron = new Cron(`*/${scheduleInterval} * * * * *`, () => __async(null, null, function* () {
16680
+ yield setCronSchedule();
16681
+ }));
16682
+ bootstrap.cron = new Cron(`* * * * * *`, () => __async(null, null, function* () {
16683
+ yield updateNode();
16684
+ yield updateEvents();
16685
+ }));
16686
+ });
16687
+
16688
+ // src/@core/core.stop.ts
16689
+ var stopService = () => __async(null, null, function* () {
16690
+ if (bootstrap.isReady) {
16691
+ bootstrap.isReady = false;
16692
+ if (bootstrap.session_xmpp) {
16524
16693
  try {
16525
- const converted = join(directory, `/temp/${Math.random().toString(36).substring(2, 15)}.converted.wav`);
16526
- execSync(`ffmpeg -y -i "${prefix}" -ar 8000 -ac 1 -sample_fmt s16 "${converted}"`, { stdio: "ignore" });
16527
- if (existsSync(converted)) {
16528
- tBuffer = readFileSync(converted);
16529
- tWav = getWavPCM16(tBuffer);
16530
- try {
16531
- unlinkSync(converted);
16532
- } catch (e) {
16533
- }
16534
- }
16694
+ yield bootstrap.session_xmpp.stop();
16535
16695
  } catch (e) {
16536
16696
  }
16697
+ bootstrap.cache.isConnected = false;
16698
+ bootstrap.cache.sigHault = true;
16699
+ bootstrap.session_xmpp = null;
16537
16700
  }
16538
- if (tWav == null) {
16539
- setWarning({ title: `EAS`, message: `Intro tone isn't a valid .WAV file or isn't in PCM 16-bit format. Converted attempt failed; please convert it then try again.` });
16540
- return null;
16541
- }
16542
- const tSamples = tWav.sampleRate != 8e3 ? getSampledPCM16(tWav.samples, tWav.sampleRate, 8e3) : tWav.samples;
16543
- buffRadio = setRadioEffect(tSamples, 8e3);
16544
- }
16545
- buffFull = buffRadio != null ? [buffRadio, new Int16Array(Math.floor(0.5 * 8e3))] : [];
16546
- buffFull.push(
16547
- setSameHeader(header, 3, 8e3, {
16548
- preMarkSec: 1.1,
16549
- gapSec: 0.5
16550
- }),
16551
- new Int16Array(Math.floor(0.5 * 8e3)),
16552
- setAttentionTone(8, 8e3),
16553
- new Int16Array(Math.floor(0.5 * 8e3)),
16554
- vRadio
16555
- );
16556
- for (let i = 0; i < 3; i++) {
16557
- buffFull.push(setSameHeader(header, 1, 8e3, { preMarkSec: 0.5, gapSec: 0.1 }));
16558
- buffFull.push(new Int16Array(Math.floor(0.5 * 8e3)));
16559
- }
16560
- const aSamples = getMergedPCM16(buffFull);
16561
- const aFinal = setNoise(aSamples, 2e-3);
16562
- const aBuffer = getPCM16(Array.from(aFinal).map((v) => ({ value: v })), 8e3);
16563
- writeFileSync(outTTS, aBuffer);
16564
- try {
16565
- unlinkSync(tmpTTS);
16566
- } catch (e) {
16567
16701
  }
16568
- return outTTS;
16569
16702
  });
16570
16703
 
16571
16704
  // src/@core/core.setNode.ts
@@ -16636,6 +16769,16 @@ var getRandomEvent = () => {
16636
16769
  return bootstrap.cache.events.features[Math.floor(Math.random() * bootstrap.cache.events.features.length)];
16637
16770
  };
16638
16771
 
16772
+ // src/@core/core.query.ts
16773
+ var query = (options) => __async(null, null, function* () {
16774
+ var _a;
16775
+ const get = yield bootstrap.database.prepare(
16776
+ `SELECT * FROM stanzas WHERE stanza LIKE ? LIMIT ?`
16777
+ ).all(`%${options.search}%`, (_a = options.max) != null ? _a : 100);
16778
+ const events = get.map((row) => JSON.parse(row.stanza));
16779
+ return events;
16780
+ });
16781
+
16639
16782
  // src/@core/core.clearEvents.ts
16640
16783
  var clearEvents = () => {
16641
16784
  bootstrap.cache.events.features = [];
@@ -16686,6 +16829,7 @@ export {
16686
16829
  getEvents,
16687
16830
  getNodes,
16688
16831
  getRandomEvent,
16832
+ query,
16689
16833
  setEasTone,
16690
16834
  setNode,
16691
16835
  setSettings,