@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.
- package/README.md +70 -22
- package/SUPPORTED_EVENTS.md +369 -0
- package/dist/cjs/index.cjs +1990 -1758
- package/dist/esm/index.mjs +1560 -1329
- package/dist/index.d.mts +44 -20
- package/dist/index.d.ts +44 -20
- package/package.json +1 -1
- package/src/@building/building.attachments.ts +42 -0
- package/src/@building/building.create.ts +2 -2
- package/src/@building/building.enhance.ts +8 -4
- package/src/@building/building.geometry.ts +1 -1
- package/src/@building/building.headers.ts +1 -1
- package/src/@building/building.office.ts +4 -4
- package/src/@building/building.properties.ts +15 -13
- package/src/@building/building.signature.ts +11 -11
- package/src/@building/building.tags.ts +2 -2
- package/src/@building/building.tracking.ts +6 -6
- package/src/@building/building.validate.ts +5 -3
- package/src/@core/core.createEvent.ts +82 -0
- package/src/@core/core.query.ts +1 -1
- package/src/@core/core.start.ts +1 -1
- package/src/@dictionaries/{dictionaries.eventActions.ts → dictionaries.actions.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventAwipAbreviations.ts → dictionaries.awips.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventCancelMessages.ts → dictionaries.cancellation.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventCauses.ts → dictionaries.causes.ts} +1 -1
- package/src/@dictionaries/{dictionaries.statusCorrelationText.ts → dictionaries.correlations.ts} +1 -1
- package/src/@dictionaries/{dictionaries.betterEventNames.ts → dictionaries.enhanced.ts} +13 -2
- package/src/@dictionaries/{dictionaries.eventTypes.ts → dictionaries.events.ts} +1 -1
- package/src/@dictionaries/{dictionaries.regExp.ts → dictionaries.expressions.ts} +1 -1
- package/src/@dictionaries/{dictionaries.hailStrings.ts → dictionaries.hail.ts} +4 -1
- package/src/@dictionaries/{dictionaries.officeICAOs.ts → dictionaries.icao.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventsMatchText.ts → dictionaries.matches.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventProducts.ts → dictionaries.products.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventRecords.ts → dictionaries.records.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventSeverity.ts → dictionaries.severity.ts} +1 -1
- package/src/@dictionaries/{dictionaries.shapefileLinks.ts → dictionaries.shapefiles.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventStatus.ts → dictionaries.status.ts} +1 -1
- package/src/@dictionaries/{dictionaries.eventTags.ts → dictionaries.tags.ts} +1 -1
- package/src/@dictionaries/{dictionaries.testSignatures.ts → dictionaries.test.ts} +3 -2
- package/src/@events/events.api.ts +19 -14
- package/src/@events/events.text.ts +5 -4
- package/src/@events/events.ugc.ts +5 -4
- package/src/@events/events.vtec.ts +5 -4
- package/src/@manager/manager.mkEvent.ts +4 -4
- package/src/@manager/manager.rmEvent.ts +3 -2
- package/src/@manager/manager.setHash.ts +1 -1
- package/src/@manager/manager.updateListener.ts +124 -0
- package/src/@modules/@database/database.cache.ts +1 -1
- package/src/@modules/@database/database.init.ts +1 -1
- package/src/@modules/@database/database.shapefiles.ts +3 -3
- package/src/@modules/@database/database.stanza.ts +2 -2
- package/src/@modules/@eas/eas.getCleanDescription.ts +91 -0
- package/src/@modules/@eas/eas.getTTS.ts +54 -0
- package/src/@modules/@eas/eas.setEasTone.ts +40 -30
- package/src/@modules/@stanza/stanza.getAwipsType.ts +3 -3
- package/src/@modules/@stanza/stanza.validate.ts +5 -5
- package/src/@modules/@utilities/utilities.getLatestIssuance.ts +32 -0
- package/src/@modules/@utilities/utilities.getSettings.ts +1 -1
- package/src/@modules/@utilities/utilities.setCronSchedule.ts +30 -2
- package/src/@modules/@utilities/utilities.setSettings.ts +1 -1
- package/src/@modules/@utilities/utilities.setWarning.ts +1 -1
- package/src/@modules/@xmpp/xmpp.xDeploy.ts +1 -1
- package/src/@modules/@xmpp/xmpp.xReconnect.ts +1 -1
- package/src/@modules/@xmpp/xmpp.xStanza.ts +1 -1
- package/src/@parsers/@hvtec/hvtec.extract.ts +9 -9
- package/src/@parsers/@pvtec/pvtec.extract.ts +13 -13
- package/src/@parsers/@text/text.getDescriptionFromProduct.ts +2 -2
- package/src/@parsers/@text/text.getEmbed.ts +60 -0
- package/src/@parsers/@ugc/ugc.extract.ts +1 -1
- package/src/@parsers/@ugc/ugc.header.ts +3 -3
- package/src/@types/{types.compiled.ts → type.compiled.ts} +1 -1
- package/src/@types/type.event.ts +6 -4
- package/src/@types/{types.webhook.ts → type.listener.ts} +11 -6
- package/src/@types/type.properties.ts +10 -8
- package/src/@types/{types.settings.ts → type.settings.ts} +7 -5
- package/src/bootstrap.ts +7 -5
- package/src/index.ts +3 -2
- package/test.js +19 -19
- package/src/@dictionaries/dictionaries.transcribedMessageReplacements.ts +0 -68
- package/src/@manager/manager.createWebhook.ts +0 -117
- package/src/@manager/manager.updateWebhooks.ts +0 -49
- /package/src/@types/{types.attributes.ts → type.attributes.ts} +0 -0
- /package/src/@types/{types.hash.ts → type.hash.ts} +0 -0
- /package/src/@types/{types.hvtec.ts → type.hvtec.ts} +0 -0
- /package/src/@types/{types.pvtec.ts → type.pvtec.ts} +0 -0
- /package/src/@types/{types.stanza.ts → type.stanza.ts} +0 -0
- /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/
|
|
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/
|
|
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/
|
|
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 {
|
|
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
|
|
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/
|
|
24
|
-
import { TypeStanzaCompiled } from '../../@types/
|
|
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/
|
|
21
|
-
import { transcribedMessageReplacements } from "../../@dictionaries/dictionaries.transcribedMessageReplacements";
|
|
20
|
+
import { TypeSettings } from "../../@types/type.settings";
|
|
22
21
|
import { bootstrap } from "../../bootstrap";
|
|
23
|
-
import {
|
|
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.
|
|
47
|
-
const prefix = settings.GlobalSettings.
|
|
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, `/
|
|
64
|
-
mkdirSync(join(directory, `/
|
|
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
|
-
|
|
70
|
-
|
|
68
|
+
if (!existsSync(join(directory, `/encoded`))) {
|
|
69
|
+
mkdirSync(join(directory, `/encoded`), { recursive: true });
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
|
|
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: `
|
|
77
|
-
})
|
|
78
|
-
return null
|
|
79
|
+
message: `EAS tone generation is not supported on macOS.`
|
|
80
|
+
});
|
|
81
|
+
return null
|
|
79
82
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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, `/
|
|
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/
|
|
21
|
-
import {
|
|
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(
|
|
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/
|
|
21
|
-
import { TypeStanzaCompiled } from "../../@types/
|
|
22
|
-
import {
|
|
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(
|
|
39
|
-
const isUGC = message.match(
|
|
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
|
+
}
|
|
@@ -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/
|
|
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/
|
|
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 { client } from '@xmpp/client';
|
|
21
|
-
import { TypeSettings } from "../../@types/
|
|
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/
|
|
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/
|
|
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";
|