@atmosx/event-product-parser 3.0.42 → 3.0.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +70 -22
  2. package/SUPPORTED_EVENTS.md +369 -0
  3. package/dist/cjs/index.cjs +1990 -1758
  4. package/dist/esm/index.mjs +1560 -1329
  5. package/dist/index.d.mts +44 -20
  6. package/dist/index.d.ts +44 -20
  7. package/package.json +1 -1
  8. package/src/@building/building.attachments.ts +42 -0
  9. package/src/@building/building.create.ts +2 -2
  10. package/src/@building/building.enhance.ts +8 -4
  11. package/src/@building/building.geometry.ts +1 -1
  12. package/src/@building/building.headers.ts +1 -1
  13. package/src/@building/building.office.ts +4 -4
  14. package/src/@building/building.properties.ts +15 -13
  15. package/src/@building/building.signature.ts +11 -11
  16. package/src/@building/building.tags.ts +2 -2
  17. package/src/@building/building.tracking.ts +6 -6
  18. package/src/@building/building.validate.ts +5 -3
  19. package/src/@core/core.createEvent.ts +82 -0
  20. package/src/@core/core.query.ts +1 -1
  21. package/src/@core/core.start.ts +1 -1
  22. package/src/@dictionaries/{dictionaries.eventActions.ts → dictionaries.actions.ts} +1 -1
  23. package/src/@dictionaries/{dictionaries.eventAwipAbreviations.ts → dictionaries.awips.ts} +1 -1
  24. package/src/@dictionaries/{dictionaries.eventCancelMessages.ts → dictionaries.cancellation.ts} +1 -1
  25. package/src/@dictionaries/{dictionaries.eventCauses.ts → dictionaries.causes.ts} +1 -1
  26. package/src/@dictionaries/{dictionaries.statusCorrelationText.ts → dictionaries.correlations.ts} +1 -1
  27. package/src/@dictionaries/{dictionaries.betterEventNames.ts → dictionaries.enhanced.ts} +13 -2
  28. package/src/@dictionaries/{dictionaries.eventTypes.ts → dictionaries.events.ts} +1 -1
  29. package/src/@dictionaries/{dictionaries.regExp.ts → dictionaries.expressions.ts} +1 -1
  30. package/src/@dictionaries/{dictionaries.hailStrings.ts → dictionaries.hail.ts} +4 -1
  31. package/src/@dictionaries/{dictionaries.officeICAOs.ts → dictionaries.icao.ts} +1 -1
  32. package/src/@dictionaries/{dictionaries.eventsMatchText.ts → dictionaries.matches.ts} +1 -1
  33. package/src/@dictionaries/{dictionaries.eventProducts.ts → dictionaries.products.ts} +1 -1
  34. package/src/@dictionaries/{dictionaries.eventRecords.ts → dictionaries.records.ts} +1 -1
  35. package/src/@dictionaries/{dictionaries.eventSeverity.ts → dictionaries.severity.ts} +1 -1
  36. package/src/@dictionaries/{dictionaries.shapefileLinks.ts → dictionaries.shapefiles.ts} +1 -1
  37. package/src/@dictionaries/{dictionaries.eventStatus.ts → dictionaries.status.ts} +1 -1
  38. package/src/@dictionaries/{dictionaries.eventTags.ts → dictionaries.tags.ts} +1 -1
  39. package/src/@dictionaries/{dictionaries.testSignatures.ts → dictionaries.test.ts} +3 -2
  40. package/src/@events/events.api.ts +19 -14
  41. package/src/@events/events.text.ts +5 -4
  42. package/src/@events/events.ugc.ts +5 -4
  43. package/src/@events/events.vtec.ts +5 -4
  44. package/src/@manager/manager.mkEvent.ts +4 -4
  45. package/src/@manager/manager.rmEvent.ts +3 -2
  46. package/src/@manager/manager.setHash.ts +1 -1
  47. package/src/@manager/manager.updateListener.ts +124 -0
  48. package/src/@modules/@database/database.cache.ts +1 -1
  49. package/src/@modules/@database/database.init.ts +1 -1
  50. package/src/@modules/@database/database.shapefiles.ts +3 -3
  51. package/src/@modules/@database/database.stanza.ts +2 -2
  52. package/src/@modules/@eas/eas.getCleanDescription.ts +91 -0
  53. package/src/@modules/@eas/eas.getTTS.ts +54 -0
  54. package/src/@modules/@eas/eas.setEasTone.ts +40 -30
  55. package/src/@modules/@stanza/stanza.getAwipsType.ts +3 -3
  56. package/src/@modules/@stanza/stanza.validate.ts +5 -5
  57. package/src/@modules/@utilities/utilities.getLatestIssuance.ts +32 -0
  58. package/src/@modules/@utilities/utilities.getSettings.ts +1 -1
  59. package/src/@modules/@utilities/utilities.setCronSchedule.ts +30 -2
  60. package/src/@modules/@utilities/utilities.setSettings.ts +1 -1
  61. package/src/@modules/@utilities/utilities.setWarning.ts +1 -1
  62. package/src/@modules/@xmpp/xmpp.xDeploy.ts +1 -1
  63. package/src/@modules/@xmpp/xmpp.xReconnect.ts +1 -1
  64. package/src/@modules/@xmpp/xmpp.xStanza.ts +1 -1
  65. package/src/@parsers/@hvtec/hvtec.extract.ts +9 -9
  66. package/src/@parsers/@pvtec/pvtec.extract.ts +13 -13
  67. package/src/@parsers/@text/text.getDescriptionFromProduct.ts +2 -2
  68. package/src/@parsers/@text/text.getEmbed.ts +60 -0
  69. package/src/@parsers/@ugc/ugc.extract.ts +1 -1
  70. package/src/@parsers/@ugc/ugc.header.ts +3 -3
  71. package/src/@types/{types.compiled.ts → type.compiled.ts} +1 -1
  72. package/src/@types/type.event.ts +6 -4
  73. package/src/@types/{types.webhook.ts → type.listener.ts} +11 -6
  74. package/src/@types/type.properties.ts +10 -8
  75. package/src/@types/{types.settings.ts → type.settings.ts} +7 -5
  76. package/src/bootstrap.ts +7 -5
  77. package/src/index.ts +3 -2
  78. package/test.js +19 -19
  79. package/src/@dictionaries/dictionaries.transcribedMessageReplacements.ts +0 -68
  80. package/src/@manager/manager.createWebhook.ts +0 -117
  81. package/src/@manager/manager.updateWebhooks.ts +0 -49
  82. /package/src/@types/{types.attributes.ts → type.attributes.ts} +0 -0
  83. /package/src/@types/{types.hash.ts → type.hash.ts} +0 -0
  84. /package/src/@types/{types.hvtec.ts → type.hvtec.ts} +0 -0
  85. /package/src/@types/{types.pvtec.ts → type.pvtec.ts} +0 -0
  86. /package/src/@types/{types.stanza.ts → type.stanza.ts} +0 -0
  87. /package/src/@types/{types.ugc.ts → type.ugc.ts} +0 -0
@@ -0,0 +1,124 @@
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 { createHttp } from "../@modules/@utilities/utilities.createHttp";
28
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from "fs";
29
+ import FormData from "form-data";
30
+
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
+ for (const socket of listeners) {
39
+ const events = socket.events;
40
+ const sWebhook = socket?.webhook;
41
+ const sUploads = socket?.uploads;
42
+
43
+
44
+ const matched = events.some(pattern => {
45
+ if (!pattern) return false;
46
+ if (pattern === "*" || pattern === properties.event) return true;
47
+ if (pattern.includes("*")) {
48
+ const regex = "^" +
49
+ pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$";
50
+ return new RegExp(regex).test(properties.event);
51
+ }
52
+ return false;
53
+ });
54
+ if (events?.length == 0 || matched) {
55
+ let eas;
56
+ let file;
57
+
58
+ if (sUploads?.eas) {
59
+ eas = await setEasTone({
60
+ title: `${properties.event}_${properties.status}_${properties.metadata.tracking}`,
61
+ message: properties.description,
62
+ header: properties.metadata.header
63
+ });
64
+ }
65
+ if (sUploads?.file) {
66
+ const destination = bootstrap.settings.GlobalSettings.ArchiveSettings.TextDirectory;
67
+ if (destination) {
68
+ if (!existsSync(destination)) { mkdirSync(destination, { recursive: true }); }
69
+ if (existsSync(`${destination}/${properties.event}_${properties.status}_${properties.metadata.tracking}.txt`)) {
70
+ appendFileSync(`${destination}/${properties.event}_${properties.status}_${properties.metadata.tracking}.txt`, properties.metadata.raw);
71
+ } else {
72
+ writeFileSync(`${destination}/${properties.event}_${properties.status}_${properties.metadata.tracking}.txt`, properties.metadata.raw);
73
+ }
74
+ }
75
+ file = properties.metadata.raw;
76
+ }
77
+
78
+ if (sWebhook?.enabled) {
79
+ const isLimited = setTimeoutAction({ identifier: sWebhook.destination, interval: sWebhook.ratelimit, max: sWebhook.ratelimit, addTime: true })
80
+ if (!isLimited.limited) {
81
+ let body = getEmebed(event);
82
+ if (body.length > 1900) {
83
+ body = body.substring(0, 1900) + "\n\n[Message truncated due to length]";
84
+ const blocks = (body.match(/```/g) ?? []).length;
85
+ if (blocks % 2 !== 0) body += "```";
86
+ }
87
+
88
+ if (properties.description.length < 25) { continue }
89
+
90
+ const form = new FormData();
91
+ const embed = {
92
+ title: `${properties.event} (${properties.status})`,
93
+ description: body,
94
+ color: 16711680,
95
+ timestamp: new Date().toISOString(),
96
+ image: {},
97
+ footer: { text: sWebhook.title ?? `AtmosphericX` }
98
+ };
99
+ form.append("payload_json", JSON.stringify({
100
+ username: sWebhook.title ?? "AtmosphericX",
101
+ content: sWebhook.message ?? "",
102
+ embeds: [embed]
103
+ }));
104
+ if (sUploads?.file) {
105
+ form.append("fUpload", Buffer.from(file), { filename: `${properties.event}_${properties.status}_${properties.metadata.tracking}.txt`, contentType: "application/text" });
106
+ }
107
+ if (sUploads?.eas) {
108
+ if (eas) {
109
+ const file = readFileSync(eas)
110
+ form.append("fEas", Buffer.from(file), { filename: `${properties.event}_${properties.status}_${properties.metadata.tracking}_eas.mp3`, contentType: "audio/mpeg" });
111
+ }
112
+ }
113
+ const a = await createHttp({
114
+ url: sWebhook.destination,
115
+ timeout: 5000,
116
+ method: `POST`,
117
+ headers: form.getHeaders(),
118
+ body: form
119
+ })
120
+ }
121
+ }
122
+ }
123
+ }
124
+ };
@@ -17,7 +17,7 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeSettings } from "../../@types/types.settings"
20
+ import { TypeSettings } from "../../@types/type.settings"
21
21
  import { bootstrap } from '../../bootstrap'
22
22
  import { setWarning } from '../@utilities/utilities.setWarning';
23
23
  import { createEvent } from '../../@building/building.create';
@@ -19,7 +19,7 @@
19
19
 
20
20
  import fs from 'fs'
21
21
  import sqlite3 from 'better-sqlite3'
22
- import { TypeSettings } from "../../@types/types.settings"
22
+ 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'
@@ -21,11 +21,11 @@ import fs from 'fs'
21
21
  import { resolve, extname} from 'path'
22
22
  import { loadAsync } from 'jszip'
23
23
  import { read } from 'shapefile'
24
- import { TypeSettings } from "../../@types/types.settings"
24
+ 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;
@@ -36,7 +36,7 @@ export const importShapefiles = async (): Promise<void> => {
36
36
  if (tShapefiles === 0) {
37
37
  await setSleep({timeout: 1e3});
38
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) {
39
+ for (const shapefile of dict_shapefiles) {
40
40
  const response = await fetch(shapefile.link);
41
41
  const arrayBuff = await response.arrayBuffer();
42
42
  const content = await loadAsync(arrayBuff);
@@ -20,8 +20,8 @@
20
20
 
21
21
  import { bootstrap } from '../../bootstrap'
22
22
  import { setWarning } from '../@utilities/utilities.setWarning';
23
- import { TypeSettings } from "../../@types/types.settings"
24
- import { TypeStanzaCompiled } from '../../@types/types.compiled';
23
+ import { TypeSettings } from "../../@types/type.settings"
24
+ import { TypeStanzaCompiled } from '../../@types/type.compiled';
25
25
 
26
26
  export const importStanza = async (stanza: TypeStanzaCompiled): Promise<void> => {
27
27
  const settings = bootstrap.settings as TypeSettings;
@@ -0,0 +1,91 @@
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
+ export const getCleanDescription = (message: string): string => {
22
+ const patches: { regex: RegExp, replacement: string }[] = [
23
+ { regex: /\.{3,}/g, replacement: ". " },
24
+ { regex: /\bUTC\b/g, replacement: 'Coordinated Universal Time' },
25
+ { regex: /\bGMT\b/g, replacement: 'Greenwich Mean Time' },
26
+ { regex: /\bEST\b(?!\w)/g, replacement: 'Eastern Standard Time' },
27
+ { regex: /\bEDT\b(?!\w)/g, replacement: 'Eastern Daylight Time' },
28
+ { regex: /\bCST\b(?!\w)/g, replacement: 'Central Standard Time' },
29
+ { regex: /\bCDT\b(?!\w)/g, replacement: 'Central Daylight Time' },
30
+ { regex: /\bMST\b(?!\w)/g, replacement: 'Mountain Standard Time' },
31
+ { regex: /\bMDT\b(?!\w)/g, replacement: 'Mountain Daylight Time' },
32
+ { regex: /\bPST\b(?!\w)/g, replacement: 'Pacific Standard Time' },
33
+ { regex: /\bPDT\b(?!\w)/g, replacement: 'Pacific Daylight Time' },
34
+ { regex: /\bAKST\b(?!\w)/g, replacement: 'Alaska Standard Time' },
35
+ { regex: /\bAKDT\b(?!\w)/g, replacement: 'Alaska Daylight Time' },
36
+ { regex: /\bHST\b(?!\w)/g, replacement: 'Hawaii Standard Time' },
37
+ { regex: /\bHDT\b(?!\w)/g, replacement: 'Hawaii Daylight Time' },
38
+ { regex: /\bmph\b(?!\w)/g, replacement: 'miles per hour' },
39
+ { regex: /\bkm\/h\b(?!\w)/g, replacement: 'kilometers per hour' },
40
+ { regex: /\bkmh\b(?!\w)/g, replacement: 'kilometers per hour' },
41
+ { regex: /\bkt\b(?!\w)/g, replacement: 'knots' },
42
+ { regex: /\bNE\b(?!\w)/g, replacement: 'northeast' },
43
+ { regex: /\bNW\b(?!\w)/g, replacement: 'northwest' },
44
+ { regex: /\bSE\b(?!\w)/g, replacement: 'southeast' },
45
+ { regex: /\bSW\b(?!\w)/g, replacement: 'southwest' },
46
+ { regex: /\bNM\b(?!\w)/g, replacement: 'nautical miles' },
47
+ { regex: /\bdeg\b(?!\w)/g, replacement: 'degrees' },
48
+ { regex: /\btstm\b(?!\w)/g, replacement: 'thunderstorm' },
49
+ { regex: /\bmm\b(?!\w)/g, replacement: 'millimeters' },
50
+ { regex: /\bcm\b(?!\w)/g, replacement: 'centimeters' },
51
+ { regex: /\bin.\b(?!\w)/g, replacement: 'inches' },
52
+ { regex: /\bft\b(?!\w)/g, replacement: 'feet' },
53
+ { regex: /\bmi\b(?!\w)/g, replacement: 'miles' },
54
+ { regex: /\bhr\b(?!\w)/g, replacement: 'hour' },
55
+ { regex: /\bhourly\b(?!\w)/g, replacement: 'per hour' },
56
+ { regex: /\bkg\b(?!\w)/g, replacement: 'kilograms' },
57
+ { regex: /\bg\/kg\b(?!\w)/g, replacement: 'grams per kilogram' },
58
+ { regex: /\bmb\b(?!\w)/g, replacement: 'millibars' },
59
+ { regex: /\bhPa\b(?!\w)/g, replacement: 'hectopascals' },
60
+ { regex: /\bPa\b(?!\w)/g, replacement: 'pascals' },
61
+ { regex: /\bKPa\b(?!\w)/g, replacement: 'kilopascals' },
62
+ { regex: /\bC\/hr\b(?!\w)/g, replacement: 'degrees Celsius per hour' },
63
+ { regex: /\bF\/hr\b(?!\w)/g, replacement: 'degrees Fahrenheit per hour' },
64
+ { regex: /\bC\/min\b(?!\w)/g, replacement: 'degrees Celsius per minute' },
65
+ { regex: /\bF\/min\b(?!\w)/g, replacement: 'degrees Fahrenheit per minute' },
66
+ { regex: /\bC\b(?!\w)/g, replacement: 'degrees Celsius' },
67
+ { regex: /\bF\b(?!\w)/g, replacement: 'degrees Fahrenheit' },
68
+ ]
69
+ const hours: Record<number, string> = {
70
+ 0: "twelve", 1: "one", 2: "two", 3: "three", 4: "four",
71
+ 5: "five", 6: "six", 7: "seven", 8: "eight", 9: "nine",
72
+ 10: "ten", 11: "eleven", 12: "twelve",
73
+ };
74
+ const minWord = (m: number) => m === 0 ? "o'clock" : m < 10 ? `oh ${m}` : `${m}`;
75
+ const fmt = (h: number, m: number, ap: string) => `${hours[h % 12]} ${minWord(m)} ${ap}`;
76
+ for (const {regex, replacement} of patches) {
77
+ message = message.replace(regex, replacement)
78
+ }
79
+ return message
80
+ .replace(/'/g, "''")
81
+ .replace(/\b(\d{1,2}):(\d{2})\s*(AM|PM)\b/gi, (_, h, m, ap) => fmt(+h, +m, ap.toUpperCase()))
82
+ .replace(/\b(\d{1,2})\s+(\d{2})\s+(AM|PM)\b/gi, (_, h, m, ap) => fmt(+h, +m, ap.toUpperCase()))
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()))
85
+ .replace(/\*+/g, " ")
86
+ .replace(/-/g, " ")
87
+ .replace(/\n/g, " ")
88
+ .trim();
89
+ }
90
+
91
+
@@ -0,0 +1,54 @@
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 { platform } from 'os'
22
+ import { writeFileSync } from "fs"
23
+ import { execSync } from 'child_process'
24
+
25
+ export const getTTS = async (text: string, outputPath: string): Promise<void> => {
26
+ const vPlatform = platform();
27
+ switch (vPlatform) {
28
+ case 'win32': {
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
+ }
44
+ break;
45
+ }
46
+ case 'linux':
47
+ try {
48
+ execSync(`espeak -w "${outputPath}" "${text.replace(/"/g, '\\"')}"`);
49
+ } catch {}
50
+ break;
51
+ default: break;
52
+ }
53
+ };
54
+
@@ -17,10 +17,9 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeSettings } from "../../@types/types.settings";
21
- import { transcribedMessageReplacements } from "../../@dictionaries/dictionaries.transcribedMessageReplacements";
20
+ import { TypeSettings } from "../../@types/type.settings";
22
21
  import { bootstrap } from "../../bootstrap";
23
- import { setSleep } from "../@utilities/utilities.setSleep"
22
+ import { setWarning } from "../@utilities/utilities.setWarning";
24
23
  import { getWavPCM16 } from "./eas.getWavPCM16";
25
24
  import { getSampledPCM16 } from "./eas.getSampledPCM16";
26
25
  import { setRadioEffect } from "./eas.setRadioEffect";
@@ -29,59 +28,70 @@ import { setAttentionTone } from "./eas.setAttentionTone";
29
28
  import { getMergedPCM16 } from "./eas.getMergedPCM16";
30
29
  import { setNoise } from "./eas.setNoise";
31
30
  import { getPCM16 } from "./eas.getPCM16";
31
+ import { getTTS } from "./eas.getTTS";
32
+ import { getCleanDescription } from "./eas.getCleanDescription";
32
33
  import { join } from 'path'
33
34
  import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs'
34
35
  import { execSync } from 'child_process'
35
36
  import { platform } from 'os'
36
- import { setWarning } from "../@utilities/utilities.setWarning";
37
- import say from 'say'
38
37
 
39
38
  interface GenerateEASOptions {
40
39
  message: string
41
40
  header: string
41
+ title: string
42
42
  }
43
43
 
44
44
  export const setEasTone = async (options: GenerateEASOptions): Promise<string> => {
45
45
  const settings = bootstrap.settings as TypeSettings;
46
- const directory = settings.GlobalSettings.EASSettings.ArchiveDirectory;
47
- const prefix = settings.GlobalSettings.EASSettings.IntroWavFile;
46
+ const directory = settings.GlobalSettings.ArchiveSettings.EasDirectory;
47
+ const prefix = settings.GlobalSettings.ArchiveSettings.EasToneout;
48
48
  let message = options.message;
49
49
  let header = options.header;
50
+ let title = options.title ?? `${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, '')}`;
51
+ if (!message || !header) {
52
+ setWarning({
53
+ title: `EAS`,
54
+ message: `Message and header are required to generate an EAS tone.`
55
+ })
56
+ return null;
57
+ }
50
58
  let buffTTS: Buffer;
51
59
  let buffRadio: any;
52
60
  let buffFull: any[] = [];
53
61
 
54
-
55
- const tmpTTS = join(directory, `/temp/${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, '')}.wav`)
56
- const outTTS = join(directory, `/output/${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, '')}.wav`)
57
- const vTTS = process.platform === `win32` ? `Microsoft David Desktop` : `en-US-GuyNerual`;
58
- const vPlatform = platform();
59
-
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
- }
66
- if (!existsSync(join(directory, `/output`))) {
67
- mkdirSync(join(directory, `/output`), { recursive: true });
65
+ if (!existsSync(join(directory, `/tts`))) {
66
+ mkdirSync(join(directory, `/tts`), { recursive: true });
68
67
  }
69
- for (const {regex, replacement} of transcribedMessageReplacements) {
70
- message = message.replace(regex, replacement)
68
+ if (!existsSync(join(directory, `/encoded`))) {
69
+ mkdirSync(join(directory, `/encoded`), { recursive: true });
71
70
  }
72
71
 
73
- if (vPlatform != `win32`) {
72
+ const tmpTTS = join(directory, `/tts/${title}.wav`)
73
+ const outTTS = join(directory, `/encoded/${title}.wav`)
74
+ const vPlatform = platform();
75
+
76
+ if (vPlatform === 'darwin') {
74
77
  setWarning({
75
78
  title: `EAS`,
76
- message: `Generation isn't supported with this OS. Please wait for further updates before trying again`
77
- })
78
- return null;
79
+ message: `EAS tone generation is not supported on macOS.`
80
+ });
81
+ return null
79
82
  }
80
-
81
- say.export(message, vTTS, 1.0, tmpTTS)
82
- while (!existsSync(tmpTTS) || (buffTTS = readFileSync(tmpTTS)).length == 0) {
83
- await setSleep({timeout: 25});
83
+
84
+
85
+ await new Promise<void>((resolve, reject) => {
86
+ const tMsg = getCleanDescription(message)
87
+ getTTS(tMsg, tmpTTS).then(() => {
88
+ resolve();
89
+ });
90
+ });
91
+ if (!existsSync(tmpTTS)) {
92
+ return null;
84
93
  }
94
+ buffTTS = readFileSync(tmpTTS);
85
95
  const vWav = getWavPCM16(buffTTS);
86
96
  const vSamples = getSampledPCM16(vWav.samples, vWav.sampleRate, 8000)
87
97
  const vRadio = setRadioEffect(vSamples, 8000)
@@ -92,7 +102,7 @@ export const setEasTone = async (options: GenerateEASOptions): Promise<string> =
92
102
 
93
103
  if (tWav == null) {
94
104
  try {
95
- const converted = join(directory, `/temp/${Math.random().toString(36).substring(2, 15)}.converted.wav`)
105
+ const converted = join(directory, `/tts/${title}.converted.wav`)
96
106
  execSync(`ffmpeg -y -i "${prefix}" -ar 8000 -ac 1 -sample_fmt s16 "${converted}"`, { stdio: 'ignore' })
97
107
  if (existsSync(converted)) {
98
108
  tBuffer = readFileSync(converted)
@@ -130,7 +140,7 @@ export const setEasTone = async (options: GenerateEASOptions): Promise<string> =
130
140
  const aFinal = setNoise(aSamples, 0.002);
131
141
  const aBuffer = getPCM16(Array.from(aFinal).map(v => ({ value: v })), 8000);
132
142
  writeFileSync(outTTS, aBuffer)
133
- try{unlinkSync(tmpTTS)} catch {}
143
+ try{ unlinkSync(tmpTTS) } catch {}
134
144
  return outTTS;
135
145
  }
136
146
 
@@ -17,8 +17,8 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeAttributes } from "../../@types/types.attributes"
21
- import { eventAwipAbreviations } from "../../@dictionaries/dictionaries.eventAwipAbreviations"
20
+ import { TypeAttributes } from "../../@types/type.attributes"
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
  }
@@ -17,9 +17,9 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeStanza } from "../../@types/types.stanza"
21
- import { TypeStanzaCompiled } from "../../@types/types.compiled"
22
- import { regExp } from "../../@dictionaries/dictionaries.regExp"
20
+ import { TypeStanza } from "../../@types/type.stanza"
21
+ import { TypeStanzaCompiled } from "../../@types/type.compiled"
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 {
@@ -0,0 +1,32 @@
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
+ export const getLatestIssuance = (): string => {
22
+ const now = new Date();
23
+ const current = now.getUTCHours() * 100 + now.getUTCMinutes();
24
+ const issuances = [100, 600, 1300, 1630, 2000];
25
+ let latest = issuances[0];
26
+ for (const issuance of issuances) {
27
+ if (current >= issuance) {
28
+ latest = issuance;
29
+ }
30
+ }
31
+ return latest.toString().padStart(4, "0");
32
+ }
@@ -17,7 +17,7 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeSettings } from '../../@types/types.settings'
20
+ import { TypeSettings } from '../../@types/type.settings'
21
21
  import { bootstrap } from "../../bootstrap"
22
22
 
23
23
  export const getSettings = (): TypeSettings => {
@@ -14,19 +14,47 @@
14
14
  Documentation: http://localhost/documentation | https://atmosphericx.scriptkitty.cafe/documentation
15
15
 
16
16
  Internal Package: @atmosx/event-product-parser
17
-
17
+
18
18
 
19
19
  */
20
20
 
21
- import { TypeSettings } from "../../@types/types.settings";
21
+ import { TypeSettings } from "../../@types/type.settings";
22
22
  import { xReconnect } from "../@xmpp/xmpp.xReconnect"
23
23
  import { bootstrap } from "../../bootstrap";
24
24
  import { createHttp } from "./utilities.createHttp";
25
25
  import { createEvent } from "../../@building/building.create";
26
26
  import { setEventEmit } from "./utilities.setEventEmit";
27
+ import { readdirSync, unlinkSync, statSync, existsSync } from "fs";
28
+ import { join } from "path";
27
29
 
28
30
  export const setCronSchedule = async (): Promise<void> => {
29
31
  const settings = bootstrap.settings as TypeSettings;
32
+
33
+ const TTL = settings.GlobalSettings.ArchiveSettings.TTL
34
+ const TTLCUT = Date.now() - TTL * 1000;
35
+
36
+ const walk = (dir: string): void => {
37
+ if (existsSync(dir)) {
38
+ const entries = readdirSync(dir, { withFileTypes: true });
39
+ for (const entry of entries) {
40
+ const fullPath = join(dir, entry.name);
41
+ if (entry.isDirectory()) { walk(fullPath); continue; }
42
+ const stats = statSync(fullPath);
43
+ if (stats.mtime.getTime() < TTLCUT) {
44
+ try {
45
+ unlinkSync(fullPath);
46
+ } catch (err) {
47
+ console.error(`Failed to delete ${fullPath}:`, err);
48
+ }
49
+ }
50
+ }
51
+ }
52
+ };
53
+ walk(settings.GlobalSettings.ArchiveSettings.TextDirectory)
54
+ walk(settings.GlobalSettings.ArchiveSettings.EasDirectory)
55
+
56
+
57
+
30
58
  if (settings.EnableWireService) {
31
59
  if (settings.NOAAWeatherWireServiceSettings.ReconnectionSettings.Enabled) {
32
60
  void xReconnect(settings.NOAAWeatherWireServiceSettings.ReconnectionSettings.ReconnectionInterval)
@@ -17,7 +17,7 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeSettings } from '../../@types/types.settings'
20
+ import { TypeSettings } from '../../@types/type.settings'
21
21
  import { bootstrap } from "../../bootstrap"
22
22
 
23
23
  export const setSettings = (newSettings: TypeSettings): TypeSettings => {
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import { bootstrap } from "../../bootstrap"
21
- import { TypeSettings } from "../../@types/types.settings";
21
+ import { TypeSettings } from "../../@types/type.settings";
22
22
 
23
23
  interface SetWarningOptions {
24
24
  title?: string
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import { client } from '@xmpp/client';
21
- import { TypeSettings } from "../../@types/types.settings";
21
+ import { TypeSettings } from "../../@types/type.settings";
22
22
  import { bootstrap } from "../../bootstrap"
23
23
  import { setWarning } from "../@utilities/utilities.setWarning";
24
24
  import { xOnline } from "./xmpp.xOnline";
@@ -17,7 +17,7 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeSettings } from "../../@types/types.settings";
20
+ import { TypeSettings } from "../../@types/type.settings";
21
21
  import { bootstrap } from "../../bootstrap"
22
22
  import { setEventEmit } from "../@utilities/utilities.setEventEmit";
23
23
  import { setWarning } from "../@utilities/utilities.setWarning";
@@ -17,7 +17,7 @@
17
17
 
18
18
  */
19
19
 
20
- import { TypeStanza } from "../../@types/types.stanza";
20
+ import { TypeStanza } from "../../@types/type.stanza";
21
21
  import { bootstrap } from "../../bootstrap";
22
22
  import { validate } from "../@stanza/stanza.validate";
23
23
  import { createEvent } from "../../@building/building.create";