@atmosx/event-product-parser 3.0.3 → 3.0.6

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 (162) hide show
  1. package/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +1 -1
  2. package/.github/README.md +163 -0
  3. package/.github/SUPPORTED_EVENTS.md +370 -0
  4. package/.github/docs/docs-functions.md +30 -0
  5. package/.github/docs/docs-general-configurations.md +209 -0
  6. package/.github/docs/docs-integrations.md +37 -0
  7. package/.github/docs/docs-listeners.md +28 -0
  8. package/.github/docs/docs-source-configuration.md +218 -0
  9. package/.github/docs/docs-technical.md +50 -0
  10. package/.github/logo.png +0 -0
  11. package/.github/samples/SAMPLE_EAS_GENERATION.wav +0 -0
  12. package/.github/samples/SAMPLE_GEOJSON.json +204 -0
  13. package/.github/samples/SAMPLE_TEXT_PRODUCT.txt +119 -0
  14. package/dist/cjs/index.cjs +4681 -13425
  15. package/dist/esm/index.mjs +3163 -11909
  16. package/package.json +4 -8
  17. package/src/@building/{building.create.ts → CreateEvent.ts} +25 -11
  18. package/src/@building/{building.clean.ts → GetCleanedEvent.ts} +2 -2
  19. package/src/@building/GetEventAttachments.ts +86 -0
  20. package/src/@building/{building.enhance.ts → GetEventEnhancedName.ts} +11 -12
  21. package/src/@building/{building.geometry.ts → GetEventGeometry.ts} +7 -7
  22. package/src/@building/{building.headers.ts → GetEventHeader.ts} +3 -3
  23. package/src/@building/{building.polygon.ts → GetEventNodes.ts} +13 -11
  24. package/src/@building/{building.office.ts → GetEventOffice.ts} +6 -6
  25. package/src/@building/GetEventProperties.ts +96 -0
  26. package/src/@building/{building.signature.ts → GetEventSignature.ts} +14 -15
  27. package/src/@building/{building.tags.ts → GetEventTags.ts} +9 -4
  28. package/src/@building/{building.tracking.ts → GetEventTracking.ts} +15 -15
  29. package/src/@building/{building.validate.ts → ValidateEvents.ts} +67 -60
  30. package/src/@core/{core.clearEvents.ts → ClearEvents.ts} +3 -4
  31. package/src/@core/{core.createListener.ts → CreateListener.ts} +3 -3
  32. package/src/@core/{core.getEvents.ts → GetEvents.ts} +1 -2
  33. package/src/@core/{core.getNodes.ts → GetNodes.ts} +1 -2
  34. package/src/@core/{core.getRandomEvent.ts → GetRandomEvent.ts} +1 -2
  35. package/src/@core/{core.getVersion.ts → GetVersion.ts} +1 -2
  36. package/src/@core/ManualEvent.ts +56 -0
  37. package/src/@core/QueryStanza.ts +39 -0
  38. package/src/@core/{core.setNode.ts → SetNode.ts} +8 -8
  39. package/src/@core/{core.start.ts → StartService.ts} +21 -21
  40. package/src/@core/{core.stop.ts → StopService.ts} +1 -1
  41. package/src/@enums/AWIPS.ts +366 -0
  42. package/src/{@dictionaries/dictionaries.eventActions.ts → @enums/Actions.ts} +1 -1
  43. package/src/{@dictionaries/dictionaries.eventCancelMessages.ts → @enums/Cancellation.ts} +2 -1
  44. package/src/{@dictionaries/dictionaries.eventCauses.ts → @enums/Causes.ts} +1 -1
  45. package/src/{@dictionaries/dictionaries.statusCorrelationText.ts → @enums/Correlations.ts} +1 -1
  46. package/src/@enums/Enhanced.ts +70 -0
  47. package/src/{@dictionaries/dictionaries.eventTypes.ts → @enums/Events.ts} +1 -1
  48. package/src/{@dictionaries/dictionaries.regExp.ts → @enums/Expressions.ts} +2 -1
  49. package/src/@enums/GlobalFilter.ts +24 -0
  50. package/src/{@dictionaries/dictionaries.hailStrings.ts → @enums/Hail.ts} +7 -2
  51. package/src/{@dictionaries/dictionaries.officeICAOs.ts → @enums/ICAO.ts} +3 -1
  52. package/src/@enums/Matches.ts +55 -0
  53. package/src/{@dictionaries/dictionaries.eventProducts.ts → @enums/Products.ts} +1 -1
  54. package/src/{@dictionaries/dictionaries.eventRecords.ts → @enums/Records.ts} +1 -1
  55. package/src/{@dictionaries/dictionaries.eventSeverity.ts → @enums/Severity.ts} +1 -1
  56. package/src/{@dictionaries/dictionaries.shapefileLinks.ts → @enums/Shapefiles.ts} +1 -1
  57. package/src/@enums/States.ts +72 -0
  58. package/src/{@dictionaries/dictionaries.eventStatus.ts → @enums/Status.ts} +1 -1
  59. package/src/@enums/Strings.ts +22 -0
  60. package/src/@enums/Tags.ts +167 -0
  61. package/src/{@dictionaries/dictionaries.testSignatures.ts → @enums/Testing.ts} +3 -2
  62. package/src/@events/{events.api.ts → ParseAPI.ts} +42 -38
  63. package/src/@events/{events.text.ts → ParseText.ts} +25 -18
  64. package/src/@events/{events.ugc.ts → ParseUGC.ts} +28 -19
  65. package/src/@events/{events.vtec.ts → ParseVTEC.ts} +21 -21
  66. package/src/@manager/CreateActions.ts +213 -0
  67. package/src/@manager/{manager.mkEvent.ts → MakeEvent.ts} +34 -30
  68. package/src/@manager/RemoveEvent.ts +70 -0
  69. package/src/@manager/{manager.setHash.ts → SetHash.ts} +8 -9
  70. package/src/@manager/{manager.updateEvents.ts → UpdateEvents.ts} +9 -9
  71. package/src/@manager/{manager.updateNodes.ts → UpdateNode.ts} +24 -17
  72. package/src/@modules/@database/{database.cache.ts → GetCachedEvents.ts} +10 -8
  73. package/src/@modules/@database/ImportBroadcastify.ts +70 -0
  74. package/src/@modules/@database/{database.shapefiles.ts → ImportShapefiles.ts} +17 -20
  75. package/src/@modules/@database/{database.stanza.ts → ImportStanza.ts} +5 -6
  76. package/src/@modules/@database/{database.init.ts → InitializeDatabase.ts} +25 -11
  77. package/src/@modules/@eas/GenerateEASMessage.ts +144 -0
  78. package/src/@modules/@eas/GetCleanDescription.ts +91 -0
  79. package/src/@modules/@eas/{eas.getFloatPCM16.ts → GetFloatPCM16.ts} +1 -1
  80. package/src/@modules/@eas/{eas.getMergedPCM16.ts → GetMergedPCM16.ts} +1 -1
  81. package/src/@modules/@eas/{eas.getPCM16.ts → GetPCM16.ts} +1 -1
  82. package/src/@modules/@eas/{eas.getPCMToFloat.ts → GetPCMToFloat.ts} +1 -1
  83. package/src/@modules/@eas/{eas.getSampledPCM16.ts → GetSampledPCM16.ts} +1 -1
  84. package/src/@modules/@eas/GetTTS.ts +54 -0
  85. package/src/@modules/@eas/{eas.getWavPCM16.ts → GetWavPCM16.ts} +1 -1
  86. package/src/@modules/@eas/{eas.setAFSK.ts → SetAFSK.ts} +1 -2
  87. package/src/@modules/@eas/{eas.setAsciiToBits.ts → SetAsciiToBits.ts} +1 -2
  88. package/src/@modules/@eas/{eas.setAttentionTone.ts → SetAttentionTone.ts} +1 -1
  89. package/src/@modules/@eas/{eas.setNoise.ts → SetNoise.ts} +6 -5
  90. package/src/@modules/@eas/{eas.setRadioEffect.ts → SetRadioEffect.ts} +6 -5
  91. package/src/@modules/@eas/{eas.setSameHeader.ts → SetSameHeader.ts} +7 -8
  92. package/src/@modules/@stanza/{stanza.getAwipsType.ts → GetAwipsType.ts} +11 -9
  93. package/src/@modules/@stanza/{stanza.validate.ts → ValidateStanza.ts} +9 -9
  94. package/src/@modules/@utilities/CreateHttp.ts +83 -0
  95. package/src/@modules/@utilities/{utilities.getFormattedTime.ts → GetFormattedTime.ts} +1 -1
  96. package/src/@modules/@utilities/GetLatestIssuance.ts +32 -0
  97. package/src/@modules/@utilities/GetMatched.ts +33 -0
  98. package/src/@modules/@utilities/{utilities.getSettings.ts → GetSettings.ts} +2 -2
  99. package/src/@modules/@utilities/{utilities.getShapeNearestPoint.ts → GetShapeNearestPoint.ts} +1 -1
  100. package/src/@modules/@utilities/{utilities.setCronSchedule.ts → SetCronSchedule.ts} +41 -12
  101. package/src/{@types/types.webhook.ts → @modules/@utilities/SetDebug.ts} +16 -6
  102. package/src/@modules/@utilities/{utilities.setEventEmit.ts → SetEventEmit.ts} +5 -5
  103. package/src/@modules/@utilities/{utilities.setListener.ts → SetListener.ts} +1 -1
  104. package/src/@modules/@utilities/{utilities.setSettings.ts → SetSettings.ts} +2 -2
  105. package/src/@modules/@utilities/{utilities.setSleep.ts → SetSleep.ts} +1 -1
  106. package/src/@modules/@utilities/{utilities.setTimeoutAction.ts → SetTimeoutAction.ts} +8 -3
  107. package/src/@modules/@utilities/{utilities.setWarning.ts → SetWarning.ts} +4 -4
  108. package/src/@modules/@xmpp/{xmpp.xDeploy.ts → DeployXMPP.ts} +13 -14
  109. package/src/@modules/@xmpp/{xmpp.xError.ts → ErrorXMPP.ts} +1 -3
  110. package/src/@modules/@xmpp/{xmpp.xOffline.ts → OfflineXMPP.ts} +3 -4
  111. package/src/@modules/@xmpp/{xmpp.xOnline.ts → OnlineXMPP.ts} +6 -5
  112. package/src/@modules/@xmpp/{xmpp.xReconnect.ts → ReconnectXMPP.ts} +8 -8
  113. package/src/@modules/@xmpp/{xmpp.xStanza.ts → StanzaXMPP.ts} +11 -11
  114. package/src/@parsers/@hvtec/{hvtec.extract.ts → HVExtract.ts} +10 -10
  115. package/src/@parsers/@pvtec/{pvtec.expires.ts → GetExpiry.ts} +1 -1
  116. package/src/@parsers/@pvtec/{pvtec.extract.ts → VTECExtract.ts} +17 -17
  117. package/src/@parsers/@text/{text.getDescriptionFromProduct.ts → GetDescriptionFromProduct.ts} +12 -5
  118. package/src/@parsers/@text/GetEmbededText.ts +56 -0
  119. package/src/@parsers/@text/{text.getPolygonFromProduct.ts → GetPolygonFromProduct.ts} +31 -10
  120. package/src/@parsers/@text/GetStringText.ts +58 -0
  121. package/src/@parsers/@text/{text.getTextFromProduct.ts → GetTextFromProduct.ts} +1 -1
  122. package/src/@parsers/@text/{text.getXML.ts → GetXML.ts} +1 -1
  123. package/src/@parsers/@ugc/GetCache.ts +24 -0
  124. package/src/@parsers/@ugc/{ugc.expiry.ts → GetExpiry.ts} +1 -1
  125. package/src/@parsers/@ugc/{ugc.locations.ts → GetLocations.ts} +35 -7
  126. package/src/@parsers/@ugc/{ugc.header.ts → GetUGCHeader.ts} +3 -3
  127. package/src/@parsers/@ugc/{ugc.coordinates.ts → GetZonePolygon.ts} +4 -3
  128. package/src/@parsers/@ugc/SetCache.ts +28 -0
  129. package/src/@parsers/@ugc/{ugc.extract.ts → UGCExtract.ts} +6 -6
  130. package/src/@types/Actions.ts +37 -0
  131. package/src/@types/{type.event.ts → Event.ts} +7 -5
  132. package/src/@types/{type.properties.ts → Properties.ts} +11 -8
  133. package/src/@types/{types.settings.ts → Settings.ts} +31 -7
  134. package/src/@types/{types.compiled.ts → StanzaCompiled.ts} +2 -1
  135. package/src/bootstrap.ts +33 -9
  136. package/src/index.ts +28 -26
  137. package/tsup.config.ts +1 -1
  138. package/README.md +0 -339
  139. package/dist/index.d.mts +0 -239
  140. package/dist/index.d.ts +0 -239
  141. package/src/@building/building.properties.ts +0 -89
  142. package/src/@dictionaries/dictionaries.betterEventNames.ts +0 -99
  143. package/src/@dictionaries/dictionaries.eventAwipAbreviations.ts +0 -364
  144. package/src/@dictionaries/dictionaries.eventTags.ts +0 -93
  145. package/src/@dictionaries/dictionaries.eventsMatchText.ts +0 -36
  146. package/src/@dictionaries/dictionaries.transcribedMessageReplacements.ts +0 -68
  147. package/src/@manager/manager.rmEvent.ts +0 -49
  148. package/src/@manager/manager.updateWebhooks.ts +0 -50
  149. package/src/@modules/@eas/eas.setEasTone.ts +0 -137
  150. package/src/@modules/@utilities/utilities.createHttp.ts +0 -85
  151. package/src/@modules/@utilities/utilities.createWebhook.ts +0 -115
  152. package/test.js +0 -91
  153. /package/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
  154. /package/{LICENSE → .github/LICENSE} +0 -0
  155. /package/{SECURITY.md → .github/SECURITY.md} +0 -0
  156. /package/src/@parsers/@ugc/{ugc.zones.ts → GetZones.ts} +0 -0
  157. /package/src/@types/{types.attributes.ts → Attributes.ts} +0 -0
  158. /package/src/@types/{types.hvtec.ts → HVTEC.ts} +0 -0
  159. /package/src/@types/{types.hash.ts → Hash.ts} +0 -0
  160. /package/src/@types/{types.stanza.ts → Stanza.ts} +0 -0
  161. /package/src/@types/{types.ugc.ts → UGC.ts} +0 -0
  162. /package/src/@types/{types.pvtec.ts → VTEC.ts} +0 -0
@@ -0,0 +1,70 @@
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/CreateHttp'
22
+ import { SetWarning } from "../@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
+ if (broadcastify.error) {
68
+ SetWarning({ message: `Error importing Broadcastify data: ${broadcastify.message}` });
69
+ }
70
+ };
@@ -17,38 +17,37 @@
17
17
 
18
18
  */
19
19
 
20
- import fs from 'fs'
20
+ import { TypeSettings } from "../../@types/Settings"
21
+ import { EnumShapefiles } from '../../@enums/Shapefiles'
22
+ import { bootstrap } from "../../bootstrap"
23
+ import { SetSleep } from '../@utilities/SetSleep'
24
+ import { SetWarning } from '../@utilities/SetWarning'
25
+ import { existsSync, mkdirSync, writeFileSync, unlinkSync, rm } from "fs"
21
26
  import { resolve, extname} from 'path'
22
27
  import { loadAsync } from 'jszip'
23
28
  import { read } from 'shapefile'
24
- import { TypeSettings } from "../../@types/types.settings"
25
- import { bootstrap } from "../../bootstrap"
26
- import { setSleep } from '../@utilities/utilities.setSleep'
27
- import { setWarning } from '../@utilities/utilities.setWarning'
28
- import { shapefileLinks } from '../../@dictionaries/dictionaries.shapefileLinks'
29
29
 
30
- export const importShapefiles = async (): Promise<void> => {
30
+ export const ImportShapefiles = async (): Promise<void> => {
31
31
  const settings = bootstrap.settings as TypeSettings;
32
32
  try {
33
33
  const tShapefiles = bootstrap.database
34
34
  .prepare(`SELECT COUNT(*) AS count FROM shapefiles`)
35
35
  .get().count;
36
36
  if (tShapefiles === 0) {
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) {
37
+ await SetSleep({timeout: 1e3});
38
+ for (const shapefile of EnumShapefiles) {
40
39
  const response = await fetch(shapefile.link);
41
40
  const arrayBuff = await response.arrayBuffer();
42
41
  const content = await loadAsync(arrayBuff);
43
42
  const directory = resolve(__dirname, `../../shapefiles`);
44
- if (!fs.existsSync(directory)) {
45
- fs.mkdirSync(directory, { recursive: true });
43
+ if (!existsSync(directory)) {
44
+ mkdirSync(directory, { recursive: true });
46
45
  }
47
46
  for (const file of Object.keys(content.files)) {
48
47
  if (file.endsWith('.shp') || file.endsWith('.dbf')) {
49
48
  const data = await content.files[file].async(`nodebuffer`)
50
49
  const output = resolve(directory, `${shapefile?.name ?? ``}_${shapefile?.id ?? ``}${extname(file)}`)
51
- fs.writeFileSync(output, data)
50
+ writeFileSync(output, data)
52
51
  }
53
52
  }
54
53
  const filepath = resolve(__dirname, '../../shapefiles', shapefile.name + '_' + shapefile.id)
@@ -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[]) => {
@@ -82,15 +81,13 @@ export const importShapefiles = async (): Promise<void> => {
82
81
  insert.run(final, location, JSON.stringify(geometry));
83
82
  }
84
83
  })
85
- fs.unlinkSync(`${filepath}.shp`)
86
- fs.unlinkSync(`${filepath}.dbf`)
87
- setWarning({ message: `Cleaned up temporary files for ${shapefile.name}_${shapefile.id}` })
84
+ unlinkSync(`${filepath}.shp`)
85
+ unlinkSync(`${filepath}.dbf`)
88
86
  transaction(features)
89
87
  }
90
- setWarning({ message: `Shapefiles have finished compiling, you can now continue and close this terminal` })
91
- fs.rm(resolve(__dirname, '../../shapefiles'), { recursive: true, force: true }, () => {});
88
+ rm(resolve(__dirname, '../../shapefiles'), { recursive: true, force: true }, () => {});
92
89
  }
93
90
  } catch (error) {
94
- setWarning( {message: `An error occurred while compiling shapefiles: ${error.message}` })
91
+ SetWarning( {message: `An error occurred while compiling shapefiles: ${error.message}` })
95
92
  }
96
93
  }
@@ -17,13 +17,12 @@
17
17
 
18
18
  */
19
19
 
20
-
20
+ import { TypeSettings } from "../../@types/Settings"
21
+ import { TypeStanzaCompiled } from '../../@types/StanzaCompiled';
21
22
  import { bootstrap } from '../../bootstrap'
22
- import { setWarning } from '../@utilities/utilities.setWarning';
23
- import { TypeSettings } from "../../@types/types.settings"
24
- import { TypeStanzaCompiled } from '../../@types/types.compiled';
23
+ import { SetWarning } from '../@utilities/SetWarning';
25
24
 
26
- export const importStanza = async (stanza: TypeStanzaCompiled): Promise<void> => {
25
+ export const ImportStanza = async (stanza: TypeStanzaCompiled): Promise<void> => {
27
26
  const settings = bootstrap.settings as TypeSettings;
28
27
  try {
29
28
  if (!settings.NOAAWeatherWireServiceSettings.CacheSettings.Enabled) { return }
@@ -43,6 +42,6 @@ export const importStanza = async (stanza: TypeStanzaCompiled): Promise<void> =>
43
42
  }
44
43
  }
45
44
  } catch (error) {
46
- setWarning({ message: `An error occurred while importing stanza: ${error.message}` })
45
+ SetWarning({ message: `An error occurred while importing stanza: ${error.message}` })
47
46
  }
48
47
  }
@@ -17,19 +17,21 @@
17
17
 
18
18
  */
19
19
 
20
- import fs from 'fs'
21
- import sqlite3 from 'better-sqlite3'
22
- import { TypeSettings } from "../../@types/types.settings"
20
+
21
+ import { TypeSettings } from "../../@types/Settings"
23
22
  import { bootstrap } from "../../bootstrap"
24
- import { setWarning } from '../@utilities/utilities.setWarning'
25
- import { importShapefiles } from './database.shapefiles'
23
+ import { SetWarning } from '../@utilities/SetWarning'
24
+ import { ImportShapefiles } from './ImportShapefiles'
25
+ import { ImportBroadcastify } from "./ImportBroadcastify"
26
+ import { existsSync, writeFileSync } from "fs"
27
+ import sqlite3 from 'better-sqlite3'
26
28
 
27
- export const initializeDatabase = async (): Promise<void> => {
29
+ export const InitializeDatabase = async (): Promise<void> => {
28
30
  const settings = bootstrap.settings as TypeSettings;
29
31
  try {
30
- if (!fs.existsSync(settings.Database)) {
31
- fs.writeFileSync(settings.Database, '')
32
- setWarning({ message: `Creating new database at ${settings.Database}` })
32
+ if (!existsSync(settings.Database)) {
33
+ writeFileSync(settings.Database, '')
34
+ SetWarning({ message: `Creating new database at ${settings.Database}` })
33
35
  }
34
36
  bootstrap.database = new sqlite3(settings.Database);
35
37
  bootstrap.database
@@ -38,8 +40,20 @@ export const initializeDatabase = async (): Promise<void> => {
38
40
  bootstrap.database
39
41
  .prepare(`CREATE TABLE IF NOT EXISTS shapefiles (id TEXT PRIMARY KEY, location TEXT, geometry TEXT)`)
40
42
  .run();
41
- await importShapefiles();
43
+ const isNeedingShapefiles = bootstrap.database
44
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='shapefiles';`)
45
+ .get();
46
+ const isNeedingBroadcastify = bootstrap.database
47
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='broadcastify';`)
48
+ .get();
49
+
50
+ if (!isNeedingShapefiles || !isNeedingBroadcastify) {
51
+ 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.` })
52
+ await ImportBroadcastify();
53
+ await ImportShapefiles();
54
+ SetWarning({ message: `Building has completed, you can now continue or close the terminal`})
55
+ }
42
56
  } catch (error) {
43
- setWarning({ message: `An error occurred while initializing the database: ${error.message}` })
57
+ SetWarning({ message: `An error occurred while initializing the database: ${error.message}` })
44
58
  }
45
59
  }
@@ -0,0 +1,144 @@
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 { TypeSettings } from "../../@types/Settings";
21
+ import { bootstrap } from "../../bootstrap";
22
+ import { SetWarning } from "../@utilities/SetWarning";
23
+ import { GetWavPCM16 } from "./GetWavPCM16";
24
+ import { GetSampledPCM16 } from "./GetSampledPCM16";
25
+ import { SetRadioEffect } from "./SetRadioEffect";
26
+ import { SetSameHeader } from "./SetSameHeader";
27
+ import { SetAttentionTone } from "./SetAttentionTone";
28
+ import { GetMergedPCM16 } from "./GetMergedPCM16";
29
+ import { SetNoise } from "./SetNoise";
30
+ import { GetPCM16 } from "./GetPCM16";
31
+ import { GetTTS } from "./GetTTS";
32
+ import { GetCleanDescription } from "./GetCleanDescription";
33
+ import { SetDebug } from "../@utilities/SetDebug";
34
+ import { join } from 'path'
35
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs'
36
+ import { execSync } from 'child_process'
37
+ import { platform } from 'os'
38
+
39
+
40
+ interface GenerateEASMessageOptions {
41
+ message: string
42
+ header: string
43
+ title: string
44
+ }
45
+
46
+ export const GenerateEASMessage = async (options: GenerateEASMessageOptions): Promise<string> => {
47
+ const tick = performance.now()
48
+ const settings = bootstrap.settings as TypeSettings;
49
+ const directory = settings.GlobalSettings.ArchiveSettings.EasDirectory;
50
+ const prefix = settings.GlobalSettings.ArchiveSettings.EasToneout;
51
+ let message = options.message;
52
+ let header = options.header;
53
+ let title = (options.title ?? `${Math.random().toString(36).substring(2, 15)}-${header.replace(/[^a-zA-Z0-9]/g, '')}`).replace(/[\\:*?"<>|]/g, "_").replace(/\s+/g, " ").trim();
54
+ if (!message || !header) {
55
+ SetWarning({
56
+ title: `EAS`,
57
+ message: `Message and header are required to generate an EAS tone.`
58
+ })
59
+ return null;
60
+ }
61
+ let buffTTS: Buffer;
62
+ let buffRadio: any;
63
+ let buffFull: any[] = [];
64
+
65
+ if (!existsSync(directory)) {
66
+ mkdirSync(directory, { recursive: true });
67
+ }
68
+ const tmpTTS = join(directory, `/${title}-tts.wav`)
69
+ const outTTS = join(directory, `/${title}.wav`)
70
+ const vPlatform = platform();
71
+
72
+ if (vPlatform === 'darwin') {
73
+ SetWarning({
74
+ title: `EAS`,
75
+ message: `EAS tone generation is not supported on macOS.`
76
+ });
77
+ return null
78
+ }
79
+
80
+
81
+ await new Promise<void>((resolve, reject) => {
82
+ const tMsg = GetCleanDescription(message)
83
+ GetTTS(tMsg, tmpTTS).then(() => {
84
+ resolve();
85
+ });
86
+ });
87
+ if (!existsSync(tmpTTS)) {
88
+ return null;
89
+ }
90
+ buffTTS = readFileSync(tmpTTS);
91
+ const vWav = GetWavPCM16(buffTTS);
92
+ const vSamples = GetSampledPCM16(vWav.samples, vWav.sampleRate, 8000)
93
+ const vRadio = SetRadioEffect(vSamples, 8000)
94
+
95
+ if (existsSync(prefix)) {
96
+ let tBuffer = readFileSync(prefix);
97
+ let tWav = GetWavPCM16(tBuffer);
98
+
99
+ if (tWav == null) {
100
+ try {
101
+ const converted = join(directory, `/${title}-tts-fixed.wav`)
102
+ execSync(`ffmpeg -y -i "${prefix}" -ar 8000 -ac 1 -sample_fmt s16 "${converted}"`, { stdio: 'ignore' })
103
+ if (existsSync(converted)) {
104
+ tBuffer = readFileSync(converted)
105
+ tWav = GetWavPCM16(tBuffer)
106
+ try { unlinkSync(converted) } catch {}
107
+ }
108
+ } catch (e) {}
109
+ }
110
+
111
+ if (tWav == null) {
112
+ SetWarning({ title: `EAS`, message: `Intro tone isn't a valid .WAV file or isn't in PCM 16-bit format. Converted attempt failed; please convert it then try again.` })
113
+ return null;
114
+ }
115
+
116
+ const tSamples = (tWav.sampleRate != 8000 ? GetSampledPCM16(tWav.samples, tWav.sampleRate, 8000) : tWav.samples)
117
+ buffRadio = SetRadioEffect(tSamples, 8000)
118
+ }
119
+
120
+ buffFull = buffRadio != null ? [buffRadio, new Int16Array(Math.floor(0.5 * 8000))] : [];
121
+ buffFull.push(
122
+ SetSameHeader(header, 3, 8000, {
123
+ preMarkSec: 1.1,
124
+ gapSec: 0.5,
125
+ }),
126
+ new Int16Array(Math.floor(0.5 * 8000)),
127
+ SetAttentionTone(8, 8000),
128
+ new Int16Array(Math.floor(0.5 * 8000)),
129
+ vRadio
130
+ )
131
+ for (let i = 0; i < 3; i++) {
132
+ buffFull.push(SetSameHeader(header, 1, 8000, { preMarkSec: 0.5, gapSec: 0.1 }))
133
+ buffFull.push(new Int16Array(Math.floor(0.5 * 8000)));
134
+ }
135
+ const aSamples = GetMergedPCM16(buffFull);
136
+ const aFinal = SetNoise(aSamples, 0.002);
137
+ const aBuffer = GetPCM16(Array.from(aFinal).map(v => ({ value: v })), 8000);
138
+ writeFileSync(outTTS, aBuffer)
139
+ try{ unlinkSync(tmpTTS) } catch {}
140
+ SetDebug({ title: `GenerateEASMessage`, message: `${Math.round(performance.now() - tick)}ms` })
141
+ return outTTS;
142
+ }
143
+
144
+
@@ -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
+
@@ -18,7 +18,7 @@
18
18
 
19
19
  */
20
20
 
21
- export const getFloatPCM16 = (float32: Float32Array): Int16Array => {
21
+ export const GetFloatPCM16 = (float32: Float32Array): Int16Array => {
22
22
  const out = new Int16Array(float32.length);
23
23
  for (let i = 0; i < float32.length; i++) {
24
24
  let v = Math.max(-1, Math.min(1, float32[i]));
@@ -18,7 +18,7 @@
18
18
 
19
19
  */
20
20
 
21
- export const getMergedPCM16 = (arrays: Int16Array[]): Int16Array => {
21
+ export const GetMergedPCM16 = (arrays: Int16Array[]): Int16Array => {
22
22
  let total = 0;
23
23
  for (const a of arrays) total += a.length;
24
24
  const out = new Int16Array(total);
@@ -18,7 +18,7 @@
18
18
 
19
19
  */
20
20
 
21
- export const getPCM16 = (samples: Record<string, number>[], sampleRate: number): Buffer => {
21
+ export const GetPCM16 = (samples: Record<string, number>[], sampleRate: number): Buffer => {
22
22
  let o = 0;
23
23
  const bytesPerSample = 2;
24
24
  const blockAlign = 1 * bytesPerSample;
@@ -18,7 +18,7 @@
18
18
 
19
19
  */
20
20
 
21
- export const getPCMToFloat = (int16: Int16Array): Float32Array => {
21
+ export const GetPCMToFloat = (int16: Int16Array): Float32Array => {
22
22
  const out = new Float32Array(int16.length);
23
23
  for (let i = 0; i < int16.length; i++) out[i] = int16[i] / 32768;
24
24
  return out;
@@ -18,7 +18,7 @@
18
18
 
19
19
  */
20
20
 
21
- export const getSampledPCM16 = (int16: Int16Array, originalRate: number, targetRate: number): Int16Array => {
21
+ export const GetSampledPCM16 = (int16: Int16Array, originalRate: number, targetRate: number): Int16Array => {
22
22
  if (originalRate === targetRate) return int16;
23
23
  const ratio = targetRate / originalRate;
24
24
  const outLen = Math.max(1, Math.round(int16.length * ratio));
@@ -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
+
@@ -25,7 +25,7 @@ interface getWavPCM16Response {
25
25
  bitsPerSample: number
26
26
  }
27
27
 
28
- export const getWavPCM16 = (buffer: Buffer): getWavPCM16Response => {
28
+ export const GetWavPCM16 = (buffer: Buffer): getWavPCM16Response => {
29
29
  if (buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WAVE") { return null; }
30
30
  let fmt = null;
31
31
  let data = null;
@@ -18,8 +18,7 @@
18
18
 
19
19
  */
20
20
 
21
-
22
- export const setAFSK = (bits: number[], sampleRate: number): Int16Array => {
21
+ export const SetAFSK = (bits: number[], sampleRate: number): Int16Array => {
23
22
  const baud = 520.83;
24
23
  const markFreq = 2083.3;
25
24
  const spaceFreq = 1562.5;
@@ -18,8 +18,7 @@
18
18
 
19
19
  */
20
20
 
21
-
22
- export const setAsciiToBits = (str: string): number[] => {
21
+ export const SetAsciiToBits = (str: string): number[] => {
23
22
  const bits = [];
24
23
  for (let i = 0; i < str.length; i++) {
25
24
  const c = str.charCodeAt(i) & 0xFF;
@@ -18,7 +18,7 @@
18
18
 
19
19
  */
20
20
 
21
- export const setAttentionTone = (ms: number, sampleRate: number): Int16Array => {
21
+ export const SetAttentionTone = (ms: number, sampleRate: number): Int16Array => {
22
22
  const len = Math.floor(ms * sampleRate);
23
23
  const out = new Int16Array(len);
24
24
  const f1 = 853;
@@ -17,15 +17,16 @@
17
17
 
18
18
 
19
19
  */
20
- import { getPCMToFloat } from "./eas.getPCMToFloat";
21
- import { getFloatPCM16 } from "./eas.getFloatPCM16";
22
20
 
23
- export const setNoise = (int16: Int16Array, noiseLevel: number = 0.02): Int16Array => {
24
- const x = getPCMToFloat(int16);
21
+ import { GetPCMToFloat } from "./GetPCMToFloat";
22
+ import { GetFloatPCM16 } from "./GetFloatPCM16";
23
+
24
+ export const SetNoise = (int16: Int16Array, noiseLevel: number = 0.02): Int16Array => {
25
+ const x = GetPCMToFloat(int16);
25
26
  for (let i = 0; i < x.length; i++) x[i] += (Math.random() * 2 - 1) * noiseLevel;
26
27
  let peak = 0;
27
28
  for (let i = 0; i < x.length; i++) peak = Math.max(peak, Math.abs(x[i]));
28
29
  if (peak > 1) for (let i = 0; i < x.length; i++) x[i] *= 0.98 / peak;
29
- return getFloatPCM16(x);
30
+ return GetFloatPCM16(x);
30
31
  }
31
32