@atmosx/event-product-parser 3.0.44 → 3.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +77 -31
  2. package/SUPPORTED_EVENTS.md +369 -0
  3. package/dist/cjs/index.cjs +681 -403
  4. package/dist/esm/index.mjs +659 -382
  5. package/dist/index.d.mts +54 -25
  6. package/dist/index.d.ts +54 -25
  7. package/package.json +1 -1
  8. package/src/@building/building.attachments.ts +83 -0
  9. package/src/@building/building.enhance.ts +7 -3
  10. package/src/@building/building.office.ts +2 -2
  11. package/src/@building/building.properties.ts +12 -11
  12. package/src/@building/building.signature.ts +11 -11
  13. package/src/@building/building.tags.ts +2 -2
  14. package/src/@building/building.tracking.ts +3 -3
  15. package/src/@building/building.validate.ts +2 -2
  16. package/src/@core/core.createEvent.ts +83 -0
  17. package/src/@dictionaries/{dictionaries.eventActions.ts → dictionaries.actions.ts} +1 -1
  18. package/src/@dictionaries/{dictionaries.eventAwipAbreviations.ts → dictionaries.awips.ts} +1 -1
  19. package/src/@dictionaries/{dictionaries.eventCancelMessages.ts → dictionaries.cancellation.ts} +2 -1
  20. package/src/@dictionaries/{dictionaries.eventCauses.ts → dictionaries.causes.ts} +1 -1
  21. package/src/@dictionaries/{dictionaries.statusCorrelationText.ts → dictionaries.correlations.ts} +1 -1
  22. package/src/@dictionaries/{dictionaries.betterEventNames.ts → dictionaries.enhanced.ts} +13 -2
  23. package/src/@dictionaries/{dictionaries.eventTypes.ts → dictionaries.events.ts} +1 -1
  24. package/src/@dictionaries/{dictionaries.regExp.ts → dictionaries.expressions.ts} +2 -1
  25. package/src/@dictionaries/{dictionaries.hailStrings.ts → dictionaries.hail.ts} +4 -1
  26. package/src/@dictionaries/{dictionaries.officeICAOs.ts → dictionaries.icao.ts} +1 -1
  27. package/src/@dictionaries/{dictionaries.eventsMatchText.ts → dictionaries.matches.ts} +1 -1
  28. package/src/@dictionaries/{dictionaries.eventProducts.ts → dictionaries.products.ts} +1 -1
  29. package/src/@dictionaries/{dictionaries.eventRecords.ts → dictionaries.records.ts} +1 -1
  30. package/src/@dictionaries/{dictionaries.eventSeverity.ts → dictionaries.severity.ts} +1 -1
  31. package/src/@dictionaries/{dictionaries.shapefileLinks.ts → dictionaries.shapefiles.ts} +1 -1
  32. package/src/@dictionaries/dictionaries.states.ts +72 -0
  33. package/src/@dictionaries/{dictionaries.eventStatus.ts → dictionaries.status.ts} +1 -1
  34. package/src/@dictionaries/{dictionaries.eventTags.ts → dictionaries.tags.ts} +1 -1
  35. package/src/@dictionaries/{dictionaries.testSignatures.ts → dictionaries.testing.ts} +3 -2
  36. package/src/@events/events.api.ts +18 -15
  37. package/src/@events/events.text.ts +2 -2
  38. package/src/@events/events.ugc.ts +2 -2
  39. package/src/@manager/manager.mkEvent.ts +3 -3
  40. package/src/@manager/manager.rmEvent.ts +3 -2
  41. package/src/@manager/manager.updateListener.ts +163 -0
  42. package/src/@modules/@database/database.broadcastify.ts +67 -0
  43. package/src/@modules/@database/database.init.ts +14 -1
  44. package/src/@modules/@database/database.shapefiles.ts +3 -6
  45. package/src/@modules/@eas/eas.getCleanDescription.ts +2 -0
  46. package/src/@modules/@eas/eas.getTTS.ts +19 -8
  47. package/src/@modules/@eas/eas.setEasTone.ts +15 -10
  48. package/src/@modules/@stanza/stanza.getAwipsType.ts +2 -2
  49. package/src/@modules/@stanza/stanza.validate.ts +3 -3
  50. package/src/@modules/@utilities/utilities.createHttp.ts +56 -47
  51. package/src/@modules/@utilities/utilities.setCronSchedule.ts +5 -2
  52. package/src/@modules/@xmpp/xmpp.xReconnect.ts +1 -1
  53. package/src/@parsers/@hvtec/hvtec.extract.ts +8 -8
  54. package/src/@parsers/@pvtec/pvtec.extract.ts +12 -12
  55. package/src/@parsers/@text/text.getDescriptionFromProduct.ts +2 -2
  56. package/src/@parsers/@text/text.getEmbed.ts +58 -0
  57. package/src/@parsers/@ugc/ugc.header.ts +3 -3
  58. package/src/@types/type.event.ts +3 -3
  59. package/src/@types/{type.webhook.ts → type.listener.ts} +11 -7
  60. package/src/@types/type.properties.ts +10 -9
  61. package/src/@types/type.settings.ts +13 -6
  62. package/src/bootstrap.ts +13 -6
  63. package/src/index.ts +2 -1
  64. package/test.js +30 -22
  65. package/src/@building/building.outlooks.ts +0 -36
  66. package/src/@manager/manager.createWebhook.ts +0 -129
  67. package/src/@manager/manager.updateWebhooks.ts +0 -49
  68. package/src/@modules/@utilities/utilities.isImageReady.ts +0 -27
@@ -0,0 +1,163 @@
1
+ /*
2
+ _ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
12
+ Discord: https://atmosphericx-discord.scriptkitty.cafe
13
+ Ko-Fi: https://ko-fi.com/k3yomi
14
+ Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Internal Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+
21
+ import { TypeEvent } from "../@types/type.event";
22
+ import { TypeListener } from "../@types/type.listener";
23
+ import { bootstrap } from "../bootstrap"
24
+ import { setEasTone } from "../@modules/@eas/eas.setEasTone"
25
+ import { setTimeoutAction } from "../@modules/@utilities/utilities.setTimeoutAction";
26
+ import { getEmebed } from "../@parsers/@text/text.getEmbed";
27
+ import { getCleanedEvent } from "../@building/building.clean"
28
+ import { createHttp } from "../@modules/@utilities/utilities.createHttp";
29
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from "fs";
30
+ import FormData from "form-data";
31
+
32
+
33
+
34
+ export const updateListener = async (event: TypeEvent): Promise<void> => {
35
+ const settings = bootstrap.settings;
36
+ const listeners = settings.ListenerSettings as TypeListener[];
37
+ const properties = event.properties;
38
+ const metadata = {
39
+ file: null,
40
+ eas: null,
41
+ name: properties.event,
42
+ status: properties.status,
43
+ description: properties.description,
44
+ tracking: properties.metadata.tracking,
45
+ header: properties.metadata.header,
46
+ raw: properties.metadata.raw,
47
+ expired: properties.status_metadata.is_expired,
48
+ attachments: properties.metadata.attachments
49
+ }
50
+
51
+ for (const listener of listeners) {
52
+ const events = listener?.events;
53
+ const webhook = listener?.webhook;
54
+ const uploads = listener?.uploads
55
+ const isMatched = events.some(pattern => {
56
+ if (!pattern) return false;
57
+ if (pattern === "*" || pattern === metadata.name) return true;
58
+ if (pattern.includes("*")) {
59
+ const regex = "^" +
60
+ pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$";
61
+ return new RegExp(regex).test(metadata.name);
62
+ }
63
+ return false;
64
+ });
65
+
66
+ if (events?.length == 0 || isMatched) {
67
+ if (uploads?.eas) {
68
+ metadata.eas = await setEasTone({
69
+ title: `${metadata.name}_${metadata.status}_${metadata.tracking}`,
70
+ message: metadata.description,
71
+ header: metadata.header
72
+ });
73
+ }
74
+
75
+ if (uploads?.file) {
76
+ const fDestination = settings.GlobalSettings.ArchiveSettings.TextDirectory;
77
+ if (fDestination) {
78
+ if (!existsSync(fDestination)) { mkdirSync(fDestination, { recursive: true }); }
79
+ if (existsSync(`${fDestination}/${metadata.name}_${metadata.status}_${metadata.tracking}.txt`)) {
80
+ appendFileSync(`${fDestination}/${metadata.name}_${metadata.status}_${metadata.tracking}.txt`, metadata.raw);
81
+ } else {
82
+ writeFileSync(`${fDestination}/${metadata.name}_${metadata.status}_${metadata.tracking}.txt`, metadata.raw);
83
+ }
84
+ }
85
+ metadata.file = metadata.raw;
86
+ }
87
+
88
+ if (uploads?.event) {
89
+ const eDestination = settings.GlobalSettings.ArchiveSettings.EventDirectory;
90
+ if (eDestination) {
91
+ if (!existsSync(eDestination)) { mkdirSync(eDestination, { recursive: true }); }
92
+ writeFileSync(`${eDestination}/${metadata.name}_${metadata.status}_${metadata.tracking}.json`, JSON.stringify(getCleanedEvent(event), null, 2));
93
+ }
94
+ }
95
+
96
+ if (webhook.enabled && webhook.destination) {
97
+ const ratelimit = setTimeoutAction({ identifier: webhook.destination, interval: webhook.ratelimit, max: webhook.ratelimit, addTime: true })
98
+ const form = new FormData();
99
+ const embed = {
100
+ title: `${metadata.name} (${metadata.status})`,
101
+ description: getEmebed(event),
102
+ fields: [],
103
+ color: 16711680,
104
+ timestamp: new Date().toISOString(),
105
+ footer: { text: webhook.title ?? `AtmosphericX` }
106
+ };
107
+
108
+ if (ratelimit.limited) { return }
109
+
110
+
111
+ if (metadata.description && !metadata.expired) {
112
+ if (metadata.description.length > 900) {
113
+ metadata.description = metadata.description.substring(0, 900) + "\n\n[Message truncated due to length]";
114
+ }
115
+ embed.fields.push({
116
+ name: "Description",
117
+ value: metadata.description ? '```' + '\n' + metadata.description.split('\n').map(l => l.trim()).filter(Boolean).join('\n') + '\n' + '```' : ""
118
+ });
119
+ }
120
+
121
+
122
+ if (metadata.attachments?.length > 0) {
123
+ metadata.attachments = metadata.attachments.slice(0, 5);
124
+ embed.fields.push({
125
+ name: "Attachments",
126
+ value: metadata.attachments.map(attachment => `- [${attachment.name.length > 45 ? attachment.name.substring(0, 45) + '...' : attachment.name}](${attachment.link})`).join('\n')
127
+ });
128
+ }
129
+
130
+ if (uploads?.file) {
131
+ form.append("fUpload", Buffer.from(metadata.file), {
132
+ filename: `${properties.event}_${properties.status}_${properties.metadata.tracking}.txt`,
133
+ contentType: "application/text"
134
+ });
135
+ }
136
+
137
+ if (uploads?.eas) {
138
+ if (metadata.eas) {
139
+ const file = readFileSync(metadata.eas)
140
+ form.append("fEas", Buffer.from(file), {
141
+ filename: `${properties.event}_${properties.status}_${properties.metadata.tracking}_eas.mp3`,
142
+ contentType: "audio/mpeg"
143
+ });
144
+ }
145
+ }
146
+
147
+ form.append("payload_json", JSON.stringify({
148
+ username: webhook.title ?? "AtmosphericX",
149
+ content: webhook.message ?? "",
150
+ embeds: [embed]
151
+ }));
152
+
153
+ await createHttp({
154
+ url: webhook.destination,
155
+ timeout: 5000,
156
+ method: `POST`,
157
+ headers: form.getHeaders(),
158
+ body: form
159
+ })
160
+ }
161
+ }
162
+ }
163
+ }
@@ -0,0 +1,67 @@
1
+ /*
2
+ _ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Created with ♥ by the AtmosphericX Team (KiyoWx, StarflightWx, & CJ Ziegler)
12
+ Discord: https://atmosphericx-discord.scriptkitty.cafe
13
+ Ko-Fi: https://ko-fi.com/k3yomi
14
+ Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
15
+
16
+ Internal Package: @atmosx/event-product-parser
17
+
18
+ */
19
+
20
+ import { bootstrap } from "../../bootstrap"
21
+ import { createHttp } from '../@utilities/utilities.createHttp'
22
+ import { setWarning } from "../@utilities/utilities.setWarning";
23
+
24
+ export const importBroadcastify = async (): Promise<void> => {
25
+ const settings = bootstrap.settings;
26
+
27
+ const isBroadcastifyImported = bootstrap.database
28
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='broadcastify';`)
29
+ .get();
30
+
31
+
32
+ if (!settings.BroadcastifySettings.BroadcastifyAttachments) { return }
33
+ if (isBroadcastifyImported) { return }
34
+
35
+ const broadcastify = await createHttp({
36
+ url: settings.BroadcastifySettings.BroadcastifyDatabase,
37
+ timeout: 5000,
38
+ });
39
+ if (!broadcastify.error) {
40
+ bootstrap.database.prepare(`CREATE TABLE broadcastify ( state TEXT, county TEXT, feed TEXT, type TEXT, link TEXT);`)
41
+ .run();
42
+ const states = JSON.parse(broadcastify.message);
43
+ const insert = bootstrap.database.prepare(`INSERT INTO broadcastify (state, county, feed, type, link) VALUES (?, ?, ?, ?, ?)`);
44
+ const transaction = bootstrap.database.transaction((rows: any[]) => {
45
+ for (const row of rows) {
46
+ insert.run(row);
47
+ }
48
+ });
49
+ const batch: any[] = [];
50
+ for (const state of states) {
51
+ setWarning({ message: `Importing ${state.counties.length} counties from ${state.state} for Broadcastify` });
52
+ for (const county of state.counties) {
53
+ const countyName = county.county;
54
+ for (const feed of county.feeds) {
55
+ batch.push([
56
+ state.state,
57
+ countyName,
58
+ feed.name,
59
+ feed.type,
60
+ feed.link
61
+ ]);
62
+ }
63
+ }
64
+ }
65
+ transaction(batch);
66
+ }
67
+ };
@@ -23,6 +23,7 @@ import { TypeSettings } from "../../@types/type.settings"
23
23
  import { bootstrap } from "../../bootstrap"
24
24
  import { setWarning } from '../@utilities/utilities.setWarning'
25
25
  import { importShapefiles } from './database.shapefiles'
26
+ import { importBroadcastify } from "./database.broadcastify"
26
27
 
27
28
  export const initializeDatabase = async (): Promise<void> => {
28
29
  const settings = bootstrap.settings as TypeSettings;
@@ -38,7 +39,19 @@ export const initializeDatabase = async (): Promise<void> => {
38
39
  bootstrap.database
39
40
  .prepare(`CREATE TABLE IF NOT EXISTS shapefiles (id TEXT PRIMARY KEY, location TEXT, geometry TEXT)`)
40
41
  .run();
41
- await importShapefiles();
42
+ const isNeedingShapefiles = bootstrap.database
43
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='shapefiles';`)
44
+ .get();
45
+ const isNeedingBroadcastify = bootstrap.database
46
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='broadcastify';`)
47
+ .get();
48
+
49
+ if (!isNeedingShapefiles || !isNeedingBroadcastify) {
50
+ setWarning({message: `Required database tables are currently building, please ${bootstrap.ansi_colors.RED}DO NOT${bootstrap.ansi_colors.RESET} close your terminal. The building will not finish and will remain incomplete. If you do mess up, you will need to delete ${settings.Database} and restart the application.` })
51
+ await importBroadcastify();
52
+ await importShapefiles();
53
+ setWarning({ message: `Building has completed, you can now continue or close the terminal`})
54
+ }
42
55
  } catch (error) {
43
56
  setWarning({ message: `An error occurred while initializing the database: ${error.message}` })
44
57
  }
@@ -25,7 +25,7 @@ import { TypeSettings } from "../../@types/type.settings"
25
25
  import { bootstrap } from "../../bootstrap"
26
26
  import { setSleep } from '../@utilities/utilities.setSleep'
27
27
  import { setWarning } from '../@utilities/utilities.setWarning'
28
- import { shapefileLinks } from '../../@dictionaries/dictionaries.shapefileLinks'
28
+ import { dict_shapefiles } from '../../@dictionaries/dictionaries.shapefiles'
29
29
 
30
30
  export const importShapefiles = async (): Promise<void> => {
31
31
  const settings = bootstrap.settings as TypeSettings;
@@ -35,8 +35,7 @@ export const importShapefiles = async (): Promise<void> => {
35
35
  .get().count;
36
36
  if (tShapefiles === 0) {
37
37
  await setSleep({timeout: 1e3});
38
- setWarning({ message: `Shapefiles are currently building, please DO NOT close your terminal. The shapefiles will not finish and will remain incomplete. If you do mess up, you will need to delete ${settings.Database} and restart the application.` })
39
- for (const shapefile of shapefileLinks) {
38
+ for (const shapefile of dict_shapefiles) {
40
39
  const response = await fetch(shapefile.link);
41
40
  const arrayBuff = await response.arrayBuffer();
42
41
  const content = await loadAsync(arrayBuff);
@@ -56,7 +55,7 @@ export const importShapefiles = async (): Promise<void> => {
56
55
  filepath,
57
56
  filepath,
58
57
  );
59
- setWarning({ message: `Importing ${features.length} features from ${shapefile.name}_${shapefile.id}` })
58
+ setWarning({ message: `Importing ${features.length} features from ${shapefile.name}_${shapefile.id} for Shapefiles` })
60
59
  const insert = bootstrap.database
61
60
  .prepare(`INSERT OR REPLACE INTO shapefiles (id, location, geometry) VALUES (?, ?, ?)`)
62
61
  const transaction = bootstrap.database.transaction((entries: any[]) => {
@@ -84,10 +83,8 @@ export const importShapefiles = async (): Promise<void> => {
84
83
  })
85
84
  fs.unlinkSync(`${filepath}.shp`)
86
85
  fs.unlinkSync(`${filepath}.dbf`)
87
- setWarning({ message: `Cleaned up temporary files for ${shapefile.name}_${shapefile.id}` })
88
86
  transaction(features)
89
87
  }
90
- setWarning({ message: `Shapefiles have finished compiling, you can now continue and close this terminal` })
91
88
  fs.rm(resolve(__dirname, '../../shapefiles'), { recursive: true, force: true }, () => {});
92
89
  }
93
90
  } catch (error) {
@@ -77,9 +77,11 @@ export const getCleanDescription = (message: string): string => {
77
77
  message = message.replace(regex, replacement)
78
78
  }
79
79
  return message
80
+ .replace(/'/g, "''")
80
81
  .replace(/\b(\d{1,2}):(\d{2})\s*(AM|PM)\b/gi, (_, h, m, ap) => fmt(+h, +m, ap.toUpperCase()))
81
82
  .replace(/\b(\d{1,2})\s+(\d{2})\s+(AM|PM)\b/gi, (_, h, m, ap) => fmt(+h, +m, ap.toUpperCase()))
82
83
  .replace(/\b(\d{2})(\d{2})\s+(AM|PM)\b/gi, (_, hh, mm, ap) => fmt(+hh, +mm, ap.toUpperCase()))
84
+ .replace(/\b(\d{1,2})\s+(AM|PM)\b/gi, (_, h, ap) => fmt(+h, 0, ap.toUpperCase()))
83
85
  .replace(/\*+/g, " ")
84
86
  .replace(/-/g, " ")
85
87
  .replace(/\n/g, " ")
@@ -19,23 +19,34 @@
19
19
  */
20
20
 
21
21
  import { platform } from 'os'
22
+ import { writeFileSync } from "fs"
22
23
  import { execSync } from 'child_process'
23
24
 
24
25
  export const getTTS = async (text: string, outputPath: string): Promise<void> => {
25
26
  const vPlatform = platform();
26
27
  switch (vPlatform) {
27
28
  case 'win32': {
28
- const command =
29
- `Add-Type -AssemblyName System.Speech;` +
30
- `$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer;` +
31
- `$speak.SetOutputToWaveFile('${outputPath}');` +
32
- `$speak.Speak('${text}');` +
33
- `$speak.Dispose();`;
34
- execSync(`powershell -Command "${command}"`, { stdio: 'ignore' });
29
+ try {
30
+ const txtPath = outputPath + ".txt";
31
+ writeFileSync(txtPath, text, "utf8");
32
+ const command = [
33
+ "Add-Type -AssemblyName System.Speech;",
34
+ `$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer;`,
35
+ `$text = Get-Content -Raw -LiteralPath '${txtPath}';`,
36
+ `$speak.SetOutputToWaveFile('${outputPath}');`,
37
+ `$speak.Speak($text);`,
38
+ `$speak.Dispose();`
39
+ ].join(" ");
40
+ execSync(`powershell -Command "${command}"`, { stdio: 'inherit' })
41
+ } catch(error) {
42
+ console.error('Error occurred while generating TTS:', error);
43
+ }
35
44
  break;
36
45
  }
37
46
  case 'linux':
38
- execSync(`espeak -w "${outputPath}" "${text.replace(/"/g, '\\"')}"`);
47
+ try {
48
+ execSync(`espeak -w "${outputPath}" "${text.replace(/"/g, '\\"')}"`);
49
+ } catch {}
39
50
  break;
40
51
  default: break;
41
52
  }
@@ -38,14 +38,16 @@ import { platform } from 'os'
38
38
  interface GenerateEASOptions {
39
39
  message: string
40
40
  header: string
41
+ title: string
41
42
  }
42
43
 
43
44
  export const setEasTone = async (options: GenerateEASOptions): Promise<string> => {
44
45
  const settings = bootstrap.settings as TypeSettings;
45
- const directory = settings.GlobalSettings.EASSettings.ArchiveDirectory;
46
- const prefix = settings.GlobalSettings.EASSettings.IntroWavFile;
46
+ const directory = settings.GlobalSettings.ArchiveSettings.EasDirectory;
47
+ const prefix = settings.GlobalSettings.ArchiveSettings.EasToneout;
47
48
  let message = options.message;
48
49
  let header = options.header;
50
+ let title = options.title ?? `${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, '')}`;
49
51
  if (!message || !header) {
50
52
  setWarning({
51
53
  title: `EAS`,
@@ -60,15 +62,15 @@ export const setEasTone = async (options: GenerateEASOptions): Promise<string> =
60
62
  if (!existsSync(directory)) {
61
63
  mkdirSync(directory, { recursive: true });
62
64
  }
63
- if (!existsSync(join(directory, `/temp`))) {
64
- mkdirSync(join(directory, `/temp`), { recursive: true });
65
+ if (!existsSync(join(directory, `/tts`))) {
66
+ mkdirSync(join(directory, `/tts`), { recursive: true });
65
67
  }
66
- if (!existsSync(join(directory, `/output`))) {
67
- mkdirSync(join(directory, `/output`), { recursive: true });
68
+ if (!existsSync(join(directory, `/encoded`))) {
69
+ mkdirSync(join(directory, `/encoded`), { recursive: true });
68
70
  }
69
71
 
70
- const tmpTTS = join(directory, `/temp/${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, '')}.wav`)
71
- const outTTS = join(directory, `/output/${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, '')}.wav`)
72
+ const tmpTTS = join(directory, `/tts/${title}.wav`)
73
+ const outTTS = join(directory, `/encoded/${title}.wav`)
72
74
  const vPlatform = platform();
73
75
 
74
76
  if (vPlatform === 'darwin') {
@@ -83,10 +85,13 @@ export const setEasTone = async (options: GenerateEASOptions): Promise<string> =
83
85
  await new Promise<void>((resolve, reject) => {
84
86
  const tMsg = getCleanDescription(message)
85
87
  getTTS(tMsg, tmpTTS).then(() => {
86
- buffTTS = readFileSync(tmpTTS);
87
88
  resolve();
88
89
  });
89
90
  });
91
+ if (!existsSync(tmpTTS)) {
92
+ return null;
93
+ }
94
+ buffTTS = readFileSync(tmpTTS);
90
95
  const vWav = getWavPCM16(buffTTS);
91
96
  const vSamples = getSampledPCM16(vWav.samples, vWav.sampleRate, 8000)
92
97
  const vRadio = setRadioEffect(vSamples, 8000)
@@ -97,7 +102,7 @@ export const setEasTone = async (options: GenerateEASOptions): Promise<string> =
97
102
 
98
103
  if (tWav == null) {
99
104
  try {
100
- const converted = join(directory, `/temp/${Math.random().toString(36).substring(2, 15)}.converted.wav`)
105
+ const converted = join(directory, `/tts/${title}.converted.wav`)
101
106
  execSync(`ffmpeg -y -i "${prefix}" -ar 8000 -ac 1 -sample_fmt s16 "${converted}"`, { stdio: 'ignore' })
102
107
  if (existsSync(converted)) {
103
108
  tBuffer = readFileSync(converted)
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import { TypeAttributes } from "../../@types/type.attributes"
21
- import { eventAwipAbreviations } from "../../@dictionaries/dictionaries.eventAwipAbreviations"
21
+ import { dict_awips } from "../../@dictionaries/dictionaries.awips"
22
22
 
23
23
  interface ValidateOptions {
24
24
  attributes: TypeAttributes
@@ -37,7 +37,7 @@ export const getAwipsType = (options: ValidateOptions): ValidiateResponse => {
37
37
  prefix: null
38
38
  }
39
39
  }
40
- for (const [prefix, type] of Object.entries(eventAwipAbreviations)) {
40
+ for (const [prefix, type] of Object.entries(dict_awips)) {
41
41
  if (attributes.awipsid.startsWith(prefix)) {
42
42
  return { type, prefix }
43
43
  }
@@ -19,7 +19,7 @@
19
19
 
20
20
  import { TypeStanza } from "../../@types/type.stanza"
21
21
  import { TypeStanzaCompiled } from "../../@types/type.compiled"
22
- import { regExp } from "../../@dictionaries/dictionaries.regExp"
22
+ import { dict_expressions } from "../../@dictionaries/dictionaries.expressions"
23
23
  import { getAwipsType } from "./stanza.getAwipsType"
24
24
 
25
25
  interface ValidateOptions {
@@ -35,8 +35,8 @@ export const validate = (options: ValidateOptions): TypeStanzaCompiled => {
35
35
  if (attributes.awipsid && attributes.awipsid.length > 1) {
36
36
  const isCapEvent = message.includes(`<?xml`);
37
37
  const isCapAreaDescription = message.includes(`<areaDesc>`)
38
- const isVTEC = message.match(regExp.pvtec) != null;
39
- const isUGC = message.match(regExp.ugc1) != null;
38
+ const isVTEC = message.match(dict_expressions.pvtec) != null;
39
+ const isUGC = message.match(dict_expressions.ugc1) != null;
40
40
  const getType = getAwipsType({ attributes: attributes})
41
41
  if (getType.type != null) {
42
42
  return {
@@ -31,55 +31,64 @@ interface CreateHttpOptions {
31
31
 
32
32
  export const createHttp = async (options: CreateHttpOptions): Promise<any> => {
33
33
  return new Promise((resolve, reject) => {
34
- const requestOptions = {
35
- url: options.url ?? `https://api.weather.gov/alerts/active`,
36
- headers: options.headers ?? {
37
- "User-Agent": "AtmosphericX",
38
- "Accept": "application/geo+json, text/plain, */*; q=0.9",
39
- "Accept-Language": "en-US,en;q=0.9"
40
- },
41
- method: options.method ?? `GET`,
42
- timeout: options.timeout ?? 10e3,
43
- proxy: options.proxy ?? null,
44
- maxRedirects: 1,
45
- };
46
- if (options.formData) {
47
- requestOptions['formData'] = options.formData;
48
- } else if (options.body) {
49
- requestOptions['body'] = options.body;
50
- }
51
-
52
- request(requestOptions, (error, response, body) => {
53
- if (error) {
54
- return resolve({
55
- error: true,
56
- options: requestOptions,
57
- status: -1,
58
- message: error.message ?? `Unknown Error`
59
- })
34
+ try {
35
+ const requestOptions = {
36
+ url: options.url ?? `https://api.weather.gov/alerts/active`,
37
+ headers: options.headers ?? {
38
+ "User-Agent": "AtmosphericX",
39
+ "Accept": "application/geo+json, text/plain, */*; q=0.9",
40
+ "Accept-Language": "en-US,en;q=0.9"
41
+ },
42
+ method: options.method ?? `GET`,
43
+ timeout: options.timeout ?? 10e3,
44
+ proxy: options.proxy ?? null,
45
+ maxRedirects: 1,
46
+ };
47
+ if (options.formData) {
48
+ requestOptions['formData'] = options.formData;
49
+ } else if (options.body) {
50
+ requestOptions['body'] = options.body;
60
51
  }
61
- if (response.statusCode < 200 || response.statusCode >= 300) {
62
- return resolve({
63
- error: true,
52
+
53
+ request(requestOptions, (error, response, body) => {
54
+ if (error) {
55
+ return resolve({
56
+ error: true,
57
+ options: requestOptions,
58
+ status: -1,
59
+ message: error.message ?? `Unknown Error`
60
+ })
61
+ }
62
+ if (response.statusCode < 200 || response.statusCode >= 300) {
63
+ return resolve({
64
+ error: true,
65
+ options: requestOptions,
66
+ status: response.statusCode ?? -1,
67
+ message: `HTTP Status Code ${response.statusCode ?? `Unknown Status Code`} (${body})`
68
+ })
69
+ }
70
+ if (body == undefined || body == null) {
71
+ return resolve({
72
+ error: true,
73
+ options: requestOptions,
74
+ status: response.statusCode ?? -1,
75
+ message: `Empty Response Body`
76
+ })
77
+ }
78
+ resolve({
79
+ error: false,
64
80
  options: requestOptions,
65
81
  status: response.statusCode ?? -1,
66
- message: `HTTP Status Code ${response.statusCode ?? `Unknown Status Code`} (${body})`
67
- })
68
- }
69
- if (body == undefined || body == null) {
70
- return resolve({
71
- error: true,
72
- options: requestOptions,
73
- status: response.statusCode ?? -1,
74
- message: `Empty Response Body`
75
- })
76
- }
77
- resolve({
78
- error: false,
79
- options: requestOptions,
80
- status: response.statusCode ?? -1,
81
- message: body
82
- })
83
- })
82
+ message: body
83
+ })
84
+ })
85
+ } catch (error) {
86
+ return resolve({
87
+ error: true,
88
+ options: {},
89
+ status: -1,
90
+ message: error.message ?? `Unknown Error`
91
+ })
92
+ }
84
93
  })
85
94
  }
@@ -30,8 +30,9 @@ import { join } from "path";
30
30
  export const setCronSchedule = async (): Promise<void> => {
31
31
  const settings = bootstrap.settings as TypeSettings;
32
32
 
33
- const TTL = settings.GlobalSettings.EASSettings.ArchiveTTL
33
+ const TTL = settings.GlobalSettings.ArchiveSettings.TTL
34
34
  const TTLCUT = Date.now() - TTL * 1000;
35
+
35
36
  const walk = (dir: string): void => {
36
37
  if (existsSync(dir)) {
37
38
  const entries = readdirSync(dir, { withFileTypes: true });
@@ -49,7 +50,9 @@ export const setCronSchedule = async (): Promise<void> => {
49
50
  }
50
51
  }
51
52
  };
52
- walk(settings.GlobalSettings.EASSettings.ArchiveDirectory)
53
+ walk(settings.GlobalSettings.ArchiveSettings.TextDirectory)
54
+ walk(settings.GlobalSettings.ArchiveSettings.EasDirectory)
55
+ walk(settings.GlobalSettings.ArchiveSettings.EventDirectory)
53
56
 
54
57
 
55
58
 
@@ -50,7 +50,7 @@ export const xReconnect = async (interval: number): Promise<void> => {
50
50
  },
51
51
  type: `reconnect`,
52
52
  error: true
53
- },
53
+ }, message: `Attempting to reconnect to XMPP Service (Reconnect Attempt ${bootstrap.cache.tReconnects})`,
54
54
  })
55
55
  await bootstrap.session_xmpp.stop().catch(() => {});
56
56
  await bootstrap.session_xmpp.start().catch(() => {});
@@ -18,22 +18,22 @@
18
18
  */
19
19
 
20
20
  import { TypeHVTEC } from "../../@types/type.hvtec";
21
- import { eventCauses } from "../../@dictionaries/dictionaries.eventCauses";
22
- import { eventRecords } from "../../@dictionaries/dictionaries.eventRecords";
23
- import { regExp } from "../../@dictionaries/dictionaries.regExp";
24
- import { eventSeverity } from "../../@dictionaries/dictionaries.eventSeverity";
21
+ import { dict_causes } from "../../@dictionaries/dictionaries.causes";
22
+ import { dict_records } from "../../@dictionaries/dictionaries.records";
23
+ import { dict_expressions } from "../../@dictionaries/dictionaries.expressions";
24
+ import { dict_severity } from "../../@dictionaries/dictionaries.severity";
25
25
 
26
26
  export const hvExtract = (message: string): TypeHVTEC[] | null => {
27
- const getHVTECs = message.match(regExp.hvtec) ?? [];
27
+ const getHVTECs = message.match(dict_expressions.hvtec) ?? [];
28
28
  const vtecs: TypeHVTEC[] = [];
29
29
  for (const vtec of getHVTECs) {
30
30
  const sub = vtec.split(`.`);
31
31
  if (sub.length < 7) continue;
32
32
  vtecs.push({
33
33
  hvtec: vtec,
34
- severity: eventSeverity[sub[1]],
35
- cause: eventCauses[sub[2]],
36
- record: eventRecords[sub[6]]
34
+ severity: dict_severity[sub[1]],
35
+ cause: dict_causes[sub[2]],
36
+ record: dict_records[sub[6]]
37
37
  })
38
38
  }
39
39
  return vtecs.length > 0 ? vtecs : null;