@atmosx/event-product-parser 2.0.1

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/src/index.ts ADDED
@@ -0,0 +1,218 @@
1
+ /*
2
+ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| "_ ` _ \ / _ \/ __| "_ \| "_ \ / _ \ "__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Written by: KiyoWx (k3yomi)
12
+ */
13
+
14
+ import * as loader from './bootstrap';
15
+ import * as types from './types';
16
+ import Utils from './@submodules/utils';
17
+ import Xmpp from './@submodules/xmpp';
18
+ import EAS from './@submodules/eas';
19
+ import Database from './@submodules/database';
20
+ import StanzaParser from './@parsers/stanza';
21
+ import EventParser from './@parsers/events';
22
+ import TextParser from './@parsers/text';
23
+ import PVtecParser from './@parsers/pvtec';
24
+ import HVtecParser from './@parsers/hvtec';
25
+ import UGCParser from './@parsers/ugc';
26
+
27
+ export class Manager {
28
+ isNoaaWeatherWireService: boolean
29
+ job: any
30
+ constructor(metadata: types.ClientSettingsTypes) { this.start(metadata) }
31
+
32
+ /**
33
+ * @function setDisplayName
34
+ * @description
35
+ * Sets the display nickname for the NWWS XMPP session. Trims the provided
36
+ * name and validates it, emitting a warning if the name is empty or invalid.
37
+ *
38
+ * @param {string} [name]
39
+ */
40
+ public setDisplayName(name?: string) {
41
+ const settings = loader.settings as types.ClientSettingsTypes;
42
+ const trimmed = name?.trim();
43
+ if (!trimmed) {
44
+ Utils.warn(loader.definitions.messages.invalid_nickname);
45
+ return;
46
+ }
47
+ settings.noaa_weather_wire_service_settings.credentials.nickname = trimmed;
48
+ }
49
+
50
+ /**
51
+ * @function getEventPolygon
52
+ * @description
53
+ * Retrieves the geographical polygon for a given event based on its
54
+ * generated geocode and UGC zones.
55
+ *
56
+ * @async
57
+ * @param {types.EventCompiled} event
58
+ * @returns {Promise<types.geometry | null>}
59
+ */
60
+ public async getEventPolygon(event: types.EventCompiled, isUnion: boolean = true): Promise<types.geometry | null> {
61
+ const hasGenerated = event.properties.geocode?.generated ?? null;
62
+ const getUgc = event.properties.geocode?.UGC ?? null;
63
+ return await EventParser.getEventGeometry(hasGenerated, {zones: getUgc}, isUnion);
64
+ }
65
+
66
+ /**
67
+ * @function createEasAudio
68
+ * @description
69
+ * Generates an EAS (Emergency Alert System) audio file using the provided
70
+ * description and header.
71
+ *
72
+ * @async
73
+ * @param {string} description
74
+ * @param {string} header
75
+ * @returns {Promise<string>}
76
+ */
77
+ public async createEasAudio(description: string, header: string): Promise<string> {
78
+ return await EAS.generateEASAudio(description, header);
79
+ }
80
+
81
+ /**
82
+ * @function getAllAlertTypes
83
+ * @description
84
+ * Generates a list of all possible alert types by combining defined
85
+ * event names with action names.
86
+ *
87
+ * @returns {string[]}
88
+ */
89
+ public getAllAlertTypes(): string[] {
90
+ const events = new Set(Object.values(loader.definitions.events));
91
+ const actions = new Set(Object.values(loader.definitions.actions));
92
+ return Array.from(events).flatMap(event =>
93
+ Array.from(actions).map(action => `${event} ${action}`)
94
+ );
95
+ }
96
+
97
+ /**
98
+ * @function searchStanzaDatabase
99
+ * @description
100
+ * Searches the stanza database for entries containing the specified query.
101
+ * Escapes SQL wildcard characters and returns results in descending order
102
+ * by ID, up to the specified limit.
103
+ *
104
+ * @async
105
+ * @param {string} query
106
+ * @param {number} [limit=250]
107
+ * @returns {Promise<string[]>}
108
+ */
109
+ public async searchStanzaDatabase(query: string, limit: number = 250): Promise<string[]> {
110
+ const escapeLike = (s: string) => s.replace(/[%_]/g, '\\$&');
111
+ const rows = await loader.cache.db
112
+ .prepare(`SELECT * FROM stanzas WHERE stanza LIKE ? ESCAPE '\\' ORDER BY id DESC LIMIT ${limit}`)
113
+ .all(`%${escapeLike(query)}%`);
114
+ return rows;
115
+ }
116
+
117
+ /**
118
+ * @function setSettings
119
+ * @description
120
+ * Merges the provided client settings into the current configuration,
121
+ * preserving nested structures.
122
+ *
123
+ * @async
124
+ * @param {types.ClientSettingsTypes} settings
125
+ * @returns {Promise<void>}
126
+ */
127
+ public async setSettings(settings: types.ClientSettingsTypes): Promise<void> {
128
+ Utils.mergeClientSettings(loader.settings, settings);
129
+ }
130
+
131
+ /**
132
+ * @function on
133
+ * @description
134
+ * Registers a callback for a specific event and returns a function
135
+ * to unregister the listener.
136
+ *
137
+ * @param {string} event
138
+ * @param {(...args: any[]) => void} callback
139
+ * @returns {() => void}
140
+ */
141
+ public on(event: string, callback: (...args: any[]) => void): () => void {
142
+ loader.cache.events.on(event, callback);
143
+ return () => loader.cache.events.off(event, callback);
144
+ }
145
+
146
+ /**
147
+ * @function start
148
+ * @description
149
+ * Initializes the client with the provided settings, starts the NWWS XMPP
150
+ * session if applicable, loads cached messages, and sets up scheduled
151
+ * tasks (cron jobs) for ongoing processing.
152
+ *
153
+ * @async
154
+ * @param {types.ClientSettingsTypes} metadata
155
+ * @returns {Promise<void>}
156
+ */
157
+ public async start(metadata: types.ClientSettingsTypes): Promise<void> {
158
+ if (!loader.cache.isReady) {
159
+ Utils.warn(loader.definitions.messages.not_ready);
160
+ return;
161
+ }
162
+ this.setSettings(metadata);
163
+ const settings = loader.settings as types.ClientSettingsTypes;
164
+ this.isNoaaWeatherWireService = settings.is_wire;
165
+ loader.cache.isReady = false;
166
+ await Database.loadDatabase();
167
+ if (this.isNoaaWeatherWireService) {
168
+ (async () => {
169
+ try {
170
+ await Xmpp.deploySession();
171
+ await Utils.loadCollectionCache();
172
+ } catch (err: unknown) {
173
+ const msg = err instanceof Error ? err.message : String(err);
174
+ Utils.warn(`Failed to initialize NWWS services: ${msg}`);
175
+ }
176
+ })();
177
+ }
178
+ Utils.handleCronJob(this.isNoaaWeatherWireService);
179
+ if (this.job) {
180
+ try { this.job.stop(); } catch { Utils.warn(`Failed to stop existing cron job.`); }
181
+ this.job = null;
182
+ }
183
+ const interval = !this.isNoaaWeatherWireService ? settings.national_weather_service_settings.interval : 5;
184
+ this.job = new loader.packages.jobs.Cron(`*/${interval} * * * * *`, () => {
185
+ Utils.handleCronJob(this.isNoaaWeatherWireService);
186
+ });
187
+ }
188
+
189
+ /**
190
+ * @function stop
191
+ * @description
192
+ * Stops active scheduled tasks (cron job) and, if connected, the NWWS
193
+ * XMPP session. Updates relevant cache flags to indicate the session
194
+ * is no longer active.
195
+ *
196
+ * @async
197
+ * @returns {Promise<void>}
198
+ */
199
+ public async stop(): Promise<void> {
200
+ loader.cache.isReady = true;
201
+ if (this.job) {
202
+ try { this.job.stop(); } catch { Utils.warn(`Failed to stop cron job.`); }
203
+ this.job = null;
204
+ }
205
+ const session = loader.cache.session;
206
+ if (session && this.isNoaaWeatherWireService) {
207
+ try { await session.stop(); } catch { Utils.warn(`Failed to stop XMPP session.`); }
208
+ loader.cache.sigHalt = true;
209
+ loader.cache.isConnected = false;
210
+ loader.cache.session = null;
211
+ this.isNoaaWeatherWireService = false;
212
+ }
213
+ }
214
+ }
215
+
216
+ export default Manager;
217
+ export type * as types from './types';
218
+ export { StanzaParser, EventParser, TextParser, PVtecParser, HVtecParser, UGCParser, EAS, Database, Utils };
package/src/types.ts ADDED
@@ -0,0 +1,259 @@
1
+ /*
2
+ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Written by: k3yomi@GitHub
12
+ */
13
+
14
+ // ----------- Settings ----------- //
15
+ interface LocalEasSettings {
16
+ directory?: string;
17
+ intro_wav?: string;
18
+ }
19
+
20
+ interface LocalAlertFilteringSettings {
21
+ events?: string[];
22
+ filtered_icao?: string[];
23
+ ignored_icao?: string[];
24
+ ugc_filter?: string[];
25
+ state_filter?: string[];
26
+ ignored_events?: string[];
27
+ check_expired?: boolean;
28
+ }
29
+
30
+ interface LocalGlobalSettings {
31
+ parent_events_only?: boolean;
32
+ better_event_parsing?: boolean;
33
+ shapefile_coordinates?: boolean;
34
+ shapefile_skip?: number;
35
+ eas_settings?: LocalEasSettings;
36
+ filtering?: LocalAlertFilteringSettings;
37
+ }
38
+
39
+ interface LocalClientReconnectionSettings {
40
+ enabled?: boolean;
41
+ interval?: number;
42
+ }
43
+
44
+ interface LocalClientCredentialSettings {
45
+ username?: string;
46
+ password?: string;
47
+ nickname?: string;
48
+ }
49
+
50
+ interface LocalCacheSettings {
51
+ enabled?: boolean;
52
+ max_db_history?: number;
53
+ use_db_for_cache?: boolean;
54
+ max_db_cache_size?: number;
55
+ }
56
+
57
+ interface LocalAlertPreferenceSettings {
58
+ disable_ugc?: boolean;
59
+ disable_vtec?: boolean;
60
+ disable_text?: boolean;
61
+ cap_only?: boolean;
62
+ }
63
+
64
+ interface LocalNoaaWeatherWireServiceSettings {
65
+ reconnection_settings?: LocalClientReconnectionSettings;
66
+ credentials?: LocalClientCredentialSettings;
67
+ cache?: LocalCacheSettings;
68
+ preferences?: LocalAlertPreferenceSettings;
69
+ }
70
+
71
+ interface LocalNationalWeatherServiceSettings {
72
+ interval?: number;
73
+ endpoint?: string;
74
+ }
75
+
76
+ // --- Exports --- //
77
+ export interface ClientSettingsTypes {
78
+ database?: string;
79
+ is_wire?: boolean;
80
+ journal?: boolean;
81
+ noaa_weather_wire_service_settings?: LocalNoaaWeatherWireServiceSettings;
82
+ national_weather_service_settings?: LocalNationalWeatherServiceSettings;
83
+ global_settings?: LocalGlobalSettings;
84
+ }
85
+
86
+
87
+ // ----------- Alert / Events ----------- //
88
+
89
+
90
+ interface LocalEventHistory {
91
+ description?: string;
92
+ issued?: string;
93
+ type?: string;
94
+ }
95
+
96
+
97
+ interface LocalEventParameters {
98
+ wmo?: string;
99
+ source?: string;
100
+ max_hail_size?: string;
101
+ max_wind_gust?: string;
102
+ damage_threat?: string;
103
+ tornado_detection?: string;
104
+ flood_detection?: string;
105
+ discussion_tornado_intensity?: string;
106
+ discussion_wind_intensity?: string;
107
+ discussion_hail_intensity?: string;
108
+ WMOidentifier?: string[];
109
+ VTEC?: string;
110
+ maxHailSize?: string;
111
+ maxWindGust?: string;
112
+ thunderstormDamageThreat?: string[];
113
+ tornadoDetection?: string[];
114
+ waterspoutDetection?: string[];
115
+ floodDetection?: string[];
116
+ AWIPSidentifier?: string[];
117
+ NWSheadline?: string[];
118
+ }
119
+
120
+ interface LocalEventProperties {
121
+ parent?: string;
122
+ event?: string;
123
+ locations?: string;
124
+ issued?: string;
125
+ expires?: string;
126
+ geocode?: {
127
+ UGC: string[],
128
+ generated?: string | null
129
+ };
130
+ description?: string;
131
+ instruction?: string;
132
+ sender_name?: string;
133
+ sender_icao?: string;
134
+ raw?: DefaultAttributesType;
135
+ parameters?: LocalEventParameters;
136
+ messageType?: string;
137
+ sent?: string;
138
+ areaDesc?: string;
139
+ action_type?: string;
140
+ is_updated?: boolean;
141
+ is_cancelled?: boolean;
142
+ is_issued?: boolean;
143
+ is_test?: boolean;
144
+ hash?: string;
145
+ tags?: string[];
146
+ details?: Record<string, any>;
147
+ }
148
+
149
+ // --- Exports --- //
150
+
151
+ export interface StanzaAttributesType {
152
+ xmlns?: string;
153
+ id?: string;
154
+ issue?: string;
155
+ ttaaii?: string;
156
+ cccc?: string;
157
+ awipsid?: string;
158
+ }
159
+ export interface DefaultAttributesType {
160
+ attributes?: {
161
+ xmlns?: string;
162
+ id?: string;
163
+ issue?: string;
164
+ ttaaii?: string;
165
+ cccc?: string;
166
+ awipsid?: string;
167
+ }
168
+ getAwip?: Record<string, string>;
169
+ awipsType?: Record<string, string>;
170
+ isCap?: boolean;
171
+ raw?: boolean;
172
+ }
173
+
174
+ export interface StanzaCompiled {
175
+ message?: string;
176
+ attributes?: DefaultAttributesType;
177
+ isCap?: boolean;
178
+ isApi?: boolean;
179
+ isCapDescription?: boolean;
180
+ isPVtec?: boolean;
181
+ isUGC?: boolean;
182
+ getAwip?: Record<string, string>;
183
+ awipsType?: Record<string, string>;
184
+ awipsPrefix?: string;
185
+ ignore?: boolean;
186
+ awipsid?: string;
187
+ }
188
+
189
+ export interface PVtecEntry {
190
+ raw?: string;
191
+ type?: string;
192
+ tracking?: string;
193
+ event?: string;
194
+ status?: string;
195
+ wmo?: string;
196
+ expires?: Date | string;
197
+ isKWNS?: boolean;
198
+ }
199
+
200
+ export interface UGCEntry {
201
+ zones?: string[];
202
+ locations?: string[];
203
+ expiry?: Date | string;
204
+ polygon?: [number, number][];
205
+ }
206
+
207
+ export interface HVtecEntry {
208
+ severity?: string,
209
+ cause?: string,
210
+ record?: string,
211
+ raw?: string,
212
+ }
213
+
214
+ export interface geometry {
215
+ type?: string;
216
+ coordinates?: [number, number][];
217
+ }
218
+
219
+
220
+ export interface EventCompiled {
221
+ performance?: number;
222
+ id?: string;
223
+ tracking?: string;
224
+ header?: string;
225
+ pvtec?: string;
226
+ hvtec?: string;
227
+ history?: LocalEventHistory[];
228
+ properties?: LocalEventProperties
229
+ geometry?: { type?: string; coordinates?: [number, number][] } | null;
230
+ }
231
+
232
+ export type EventProperties = LocalEventProperties;
233
+ export type StanzaAttributes = DefaultAttributesType;
234
+
235
+
236
+ // ----------- Generic ----------- //
237
+
238
+ // --- Exports --- //
239
+ export type Coordinates = {
240
+ lon: number;
241
+ lat: number;
242
+ }
243
+
244
+ export type HTTPSettings = {
245
+ timeout?: number;
246
+ headers?: Record<string, string>;
247
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
248
+ body?: string;
249
+ }
250
+
251
+ export interface EnhancedEventCondition {
252
+ description?: string;
253
+ condition?: (value: string) => boolean;
254
+ }
255
+
256
+ export interface GenericHTTPResponse {
257
+ error?: boolean,
258
+ message?: { features: Record<string, any>[] } | string,
259
+ }