atmosx-nwws-parser 1.0.2

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 (84) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +6 -0
  3. package/dist/cjs/bootstrap.cjs +1009 -0
  4. package/dist/cjs/database.cjs +1114 -0
  5. package/dist/cjs/dictionaries/awips.cjs +379 -0
  6. package/dist/cjs/dictionaries/events.cjs +139 -0
  7. package/dist/cjs/dictionaries/icao.cjs +265 -0
  8. package/dist/cjs/dictionaries/offshore.cjs +40 -0
  9. package/dist/cjs/dictionaries/signatures.cjs +132 -0
  10. package/dist/cjs/eas.cjs +2857 -0
  11. package/dist/cjs/helper.cjs +3014 -0
  12. package/dist/cjs/parsers/events.cjs +2857 -0
  13. package/dist/cjs/parsers/stanza.cjs +1108 -0
  14. package/dist/cjs/parsers/text.cjs +1142 -0
  15. package/dist/cjs/parsers/types/api.cjs +2857 -0
  16. package/dist/cjs/parsers/types/cap.cjs +2857 -0
  17. package/dist/cjs/parsers/types/text.cjs +2857 -0
  18. package/dist/cjs/parsers/types/ugc.cjs +2857 -0
  19. package/dist/cjs/parsers/types/vtec.cjs +2857 -0
  20. package/dist/cjs/parsers/ugc.cjs +1139 -0
  21. package/dist/cjs/parsers/vtec.cjs +1060 -0
  22. package/dist/cjs/types.cjs +17 -0
  23. package/dist/cjs/utils.cjs +2857 -0
  24. package/dist/cjs/xmpp.cjs +2857 -0
  25. package/dist/esm/bootstrap.mjs +972 -0
  26. package/dist/esm/database.mjs +1079 -0
  27. package/dist/esm/dictionaries/awips.mjs +355 -0
  28. package/dist/esm/dictionaries/events.mjs +111 -0
  29. package/dist/esm/dictionaries/icao.mjs +241 -0
  30. package/dist/esm/dictionaries/offshore.mjs +16 -0
  31. package/dist/esm/dictionaries/signatures.mjs +106 -0
  32. package/dist/esm/eas.mjs +2824 -0
  33. package/dist/esm/helper.mjs +2974 -0
  34. package/dist/esm/parsers/events.mjs +2824 -0
  35. package/dist/esm/parsers/stanza.mjs +1072 -0
  36. package/dist/esm/parsers/text.mjs +1106 -0
  37. package/dist/esm/parsers/types/api.mjs +2824 -0
  38. package/dist/esm/parsers/types/cap.mjs +2824 -0
  39. package/dist/esm/parsers/types/text.mjs +2824 -0
  40. package/dist/esm/parsers/types/ugc.mjs +2824 -0
  41. package/dist/esm/parsers/types/vtec.mjs +2824 -0
  42. package/dist/esm/parsers/ugc.mjs +1104 -0
  43. package/dist/esm/parsers/vtec.mjs +1025 -0
  44. package/dist/esm/types.mjs +0 -0
  45. package/dist/esm/utils.mjs +2824 -0
  46. package/dist/esm/xmpp.mjs +2824 -0
  47. package/package.json +47 -0
  48. package/shapefiles/FireCounties.dbf +0 -0
  49. package/shapefiles/FireCounties.shp +0 -0
  50. package/shapefiles/FireZones.dbf +0 -0
  51. package/shapefiles/FireZones.shp +0 -0
  52. package/shapefiles/ForecastZones.dbf +0 -0
  53. package/shapefiles/ForecastZones.shp +0 -0
  54. package/shapefiles/Marine.dbf +0 -0
  55. package/shapefiles/Marine.shp +0 -0
  56. package/shapefiles/OffShoreZones.dbf +0 -0
  57. package/shapefiles/OffShoreZones.shp +0 -0
  58. package/shapefiles/USCounties.dbf +0 -0
  59. package/shapefiles/USCounties.shp +0 -0
  60. package/src/bootstrap.ts +171 -0
  61. package/src/database.ts +99 -0
  62. package/src/dictionaries/awips.ts +351 -0
  63. package/src/dictionaries/events.ts +109 -0
  64. package/src/dictionaries/icao.ts +237 -0
  65. package/src/dictionaries/offshore.ts +12 -0
  66. package/src/dictionaries/signatures.ts +103 -0
  67. package/src/eas.ts +428 -0
  68. package/src/helper.ts +167 -0
  69. package/src/parsers/events.ts +289 -0
  70. package/src/parsers/stanza.ts +103 -0
  71. package/src/parsers/text.ts +167 -0
  72. package/src/parsers/types/api.ts +94 -0
  73. package/src/parsers/types/cap.ts +89 -0
  74. package/src/parsers/types/text.ts +54 -0
  75. package/src/parsers/types/ugc.ts +85 -0
  76. package/src/parsers/types/vtec.ts +60 -0
  77. package/src/parsers/ugc.ts +148 -0
  78. package/src/parsers/vtec.ts +66 -0
  79. package/src/types.ts +187 -0
  80. package/src/utils.ts +216 -0
  81. package/src/xmpp.ts +123 -0
  82. package/test.js +1 -0
  83. package/tsconfig.json +14 -0
  84. package/tsup.config.ts +11 -0
package/src/types.ts ADDED
@@ -0,0 +1,187 @@
1
+ /*
2
+ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Written by: k3yomi@GitHub
12
+ */
13
+
14
+ interface GlobalSettings {
15
+ useParentEvents: boolean,
16
+ betterEventParsing: boolean
17
+ easSettings: {
18
+ easAlerts: string[],
19
+ easDirectory: string,
20
+ easIntroWav: string | null
21
+ },
22
+ alertFiltering: {
23
+ filteredEvents: string[],
24
+ filteredICOAs: string[],
25
+ ignoredICOAs: string[],
26
+ ugcFilter: string[],
27
+ stateFilter: string[],
28
+ ignoredEvents: string[],
29
+ checkExpired: boolean
30
+ },
31
+ }
32
+
33
+ interface DefaultParameters {
34
+ wmo: string,
35
+ source: string,
36
+ max_hail_size: string,
37
+ max_wind_gust: string,
38
+ damage_threat: string,
39
+ tornado_detection: string,
40
+ flood_detection: string,
41
+ discussion_tornado_intensity: string,
42
+ discussion_wind_intensity: string,
43
+ discussion_hail_intensity: string
44
+ WMOidentifier?: string[],
45
+ VTEC?: string,
46
+ maxHailSize?: string,
47
+ maxWindGust?: string,
48
+ thunderstormDamageThreat?: string[],
49
+ tornadoDetection?: string[],
50
+ waterspoutDetection?: string[],
51
+ floodDetection?: string[],
52
+ AWIPSidentifier?: string[],
53
+ NWSheadline?: string[],
54
+ }
55
+
56
+ interface DefaultAttributes {
57
+ xmlns?: string,
58
+ id?: string,
59
+ issue?: string,
60
+ ttaaii?: string,
61
+ cccc?: string,
62
+ awipsid?: string,
63
+ getAwip?: Record<string, string>
64
+ }
65
+
66
+ interface ClientReconnectSettings {
67
+ canReconnect: boolean,
68
+ currentInterval: number
69
+ }
70
+
71
+ interface ClientCredentialSettings {
72
+ username: string,
73
+ password: string,
74
+ nickname: string
75
+ }
76
+
77
+ interface ClientCacheSettings {
78
+ read: boolean,
79
+ maxSizeMB: number,
80
+ directory: string,
81
+ maxHistory: number
82
+ }
83
+
84
+ interface ClientAlertSettings {
85
+ isCapOnly: boolean,
86
+ isShapefileUGC: boolean
87
+ }
88
+
89
+ interface NoaaWeatherWireServiceSettings {
90
+ clientReconnections: ClientReconnectSettings,
91
+ clientCredentials: ClientCredentialSettings,
92
+ cache: ClientCacheSettings,
93
+ alertPreferences: ClientAlertSettings
94
+ }
95
+
96
+ interface NationalWeatherServiceSettings {
97
+ checkInterval: number,
98
+ endpoint: string
99
+ }
100
+
101
+ export interface EnhancedEventCondition {
102
+ description?: string;
103
+ condition?: (value: string) => boolean;
104
+ }
105
+
106
+ export interface TypeAttributes {
107
+ awipsType?: Record<string, string>,
108
+ isCap: boolean,
109
+ awipsid?: string,
110
+ raw: boolean,
111
+ issue?: string,
112
+ type?: string,
113
+ prefix?: string,
114
+ getAwip?: { type: string, prefix: string }
115
+ }
116
+
117
+ export interface NationalWeatherServiceResponse {
118
+ error: boolean,
119
+ message?: { features: TypeAlert[] }
120
+ }
121
+
122
+ export interface VTECParsed {
123
+ raw: string,
124
+ tracking: string,
125
+ event: string,
126
+ status: string,
127
+ wmo: string,
128
+ expires: string
129
+ }
130
+ export interface UGCParsed {
131
+ zones: string[],
132
+ locations: string[],
133
+ expiry: Date | null,
134
+ polygon: [number, number][]
135
+ }
136
+
137
+ export interface BaseProperties {
138
+ event?: string,
139
+ locations: string,
140
+ issued: string,
141
+ expires: string,
142
+ geocode: { UGC: string[] },
143
+ description: string,
144
+ sender_name: string,
145
+ sender_icao: string,
146
+ attributes: DefaultAttributes,
147
+ parameters: DefaultParameters,
148
+ geometry: { type?: string, coordinates?: [number, number][] } | null
149
+ messageType?: string,
150
+ sent?: string,
151
+ areaDesc?: string,
152
+ }
153
+
154
+ export interface TypeAlert {
155
+ performance: number,
156
+ tracking: string,
157
+ header: string,
158
+ vtec: string,
159
+ history: { description: string, issued: string, type: string }[],
160
+ properties: BaseProperties
161
+ geometry: { type?: string, coordinates?: [number, number][] } | null
162
+ }
163
+
164
+ export interface ClientSettings {
165
+ database?: string,
166
+ isNWWS: boolean,
167
+ NoaaWeatherWireService: NoaaWeatherWireServiceSettings,
168
+ NationalWeatherService: NationalWeatherServiceSettings,
169
+ global: GlobalSettings,
170
+ }
171
+
172
+ export interface TypeCompiled {
173
+ message: string | null,
174
+ attributes: DefaultAttributes,
175
+ isCap: boolean,
176
+ isApi: boolean,
177
+ isCapDescription: boolean,
178
+ isVtec: boolean,
179
+ isUGC: boolean,
180
+ getAwip?: { type: string, prefix: string },
181
+ awipsType: { type: string, prefix: string } | null,
182
+ awipsPrefix?: string,
183
+ ignore: boolean,
184
+ }
185
+
186
+
187
+ export type HTTPSettings = { timeout?: number, headers?: Record<string, string> }
package/src/utils.ts ADDED
@@ -0,0 +1,216 @@
1
+ /*
2
+ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| "_ ` _ \ / _ \/ __| "_ \| "_ \ / _ \ "__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Written by: KiyoWx (k3yomi)
12
+ */
13
+
14
+
15
+ import * as loader from './bootstrap';
16
+ import * as types from './types';
17
+ import StanzaParser from './parsers/stanza';
18
+ import EventParser from './parsers/events';
19
+ import Xmpp from './xmpp';
20
+
21
+ export class Utils {
22
+
23
+ /**
24
+ * Zzzzzzz... yeah not much to explain here. Simple sleep function that returns a promise after the specified milliseconds.
25
+ *
26
+ * @public
27
+ * @static
28
+ * @async
29
+ * @param {number} ms
30
+ * @returns {Promise<void>}
31
+ */
32
+ public static async sleep(ms: number): Promise<void> {
33
+ return new Promise(resolve => setTimeout(resolve, ms));
34
+ }
35
+
36
+ /**
37
+ * loadCollectionCache reads cached alert files from the specified cache directory and processes them.
38
+ *
39
+ * @public
40
+ * @static
41
+ * @async
42
+ * @returns {Promise<void>}
43
+ */
44
+ public static async loadCollectionCache(): Promise<void> {
45
+ try {
46
+ const settings = loader.settings as types.ClientSettings;
47
+ if (settings.NoaaWeatherWireService.cache.read && settings.NoaaWeatherWireService.cache.directory) {
48
+ if (!loader.packages.fs.existsSync(settings.NoaaWeatherWireService.cache.directory)) return;
49
+ const cacheDir = settings.NoaaWeatherWireService.cache.directory;
50
+ const getAllFiles = loader.packages.fs.readdirSync(cacheDir).filter((file: string) => file.endsWith('.bin') && file.startsWith('cache-'));
51
+ for (const file of getAllFiles) {
52
+ const start = Date.now();
53
+ const filepath = loader.packages.path.join(cacheDir, file);
54
+ const readFile = loader.packages.fs.readFileSync(filepath, { encoding: 'utf-8' });
55
+ const isCap = readFile.includes(`<?xml`);
56
+ if (isCap && !settings.NoaaWeatherWireService.alertPreferences.isCapOnly) continue;
57
+ if (!isCap && settings.NoaaWeatherWireService.alertPreferences.isCapOnly) continue;
58
+ const validate = StanzaParser.validate(readFile, { awipsid: file, isCap: isCap, raw: true, issue: undefined });
59
+ await EventParser.eventHandler(validate);
60
+ }
61
+ }
62
+ } catch (error: any) {
63
+ loader.cache.events.emit('onError', { code: 'error-load-cache', message: `Failed to load cache: ${error.message}`});
64
+ }
65
+ }
66
+
67
+ /**
68
+ * loadGeoJsonData fetches GeoJSON data from the National Weather Service endpoint and processes each alert.
69
+ *
70
+ * @public
71
+ * @static
72
+ * @async
73
+ * @returns {Promise<void>}
74
+ */
75
+ public static async loadGeoJsonData(): Promise<void> {
76
+ try {
77
+ const settings = loader.settings as types.ClientSettings;
78
+ const response = await this.createHttpRequest(settings.NationalWeatherService.endpoint) as types.NationalWeatherServiceResponse
79
+ if (!response.error) {
80
+ EventParser.eventHandler({message: JSON.stringify(response.message), attributes: {}, isCap: true, isApi: true, isVtec: false, isUGC: false, isCapDescription: false, awipsType: { type: 'api', prefix: 'AP' }, ignore: false});
81
+ }
82
+ } catch (error: any) {
83
+ loader.cache.events.emit('onError', { code: 'error-fetching-nws-data', message: `Failed to fetch NWS data: ${error.message}`});
84
+ }
85
+ }
86
+
87
+ /**
88
+ * detectUncaughtExceptions sets up a global handler for uncaught exceptions in the Node.js process,
89
+ *
90
+ * @public
91
+ * @static
92
+ */
93
+ public static detectUncaughtExceptions(): void {
94
+ if (process.listeners('uncaughtException').some(l => l.name === 'uncaughtExceptionHandler')) return;
95
+ process.on(`uncaughtException`, (error: Error) => {
96
+ loader.cache.events.emit(`onError`, {message: `Uncaught Exception: ${error.message}`, code: `error-uncaught-exception`, stack: error.stack});
97
+ })
98
+ }
99
+
100
+ /**
101
+ * createHttpRequest performs an HTTP GET request to the specified URL with optional settings.
102
+ *
103
+ * @public
104
+ * @static
105
+ * @async
106
+ * @param {string} url
107
+ * @param {?types.HTTPSettings} [options]
108
+ * @returns {unknown}
109
+ */
110
+ public static async createHttpRequest(url: string, options?: types.HTTPSettings) {
111
+ const defaultOptions = {
112
+ timeout: 10000,
113
+ headers: {
114
+ "User-Agent": "AtmosphericX",
115
+ "Accept": "application/geo+json, text/plain, */*; q=0.9",
116
+ "Accept-Language": "en-US,en;q=0.9"
117
+ }
118
+ };
119
+ const requestOptions = {
120
+ ...defaultOptions,
121
+ ...options,
122
+ headers: { ...defaultOptions.headers, ...(options?.headers ?? {}) }
123
+ };
124
+ try {
125
+ const resp = await loader.packages.axios.get(url, {
126
+ headers: requestOptions.headers,
127
+ timeout: requestOptions.timeout,
128
+ maxRedirects: 0,
129
+ validateStatus: (status) => status === 200 || status === 500
130
+ });
131
+ return { error: false, message: resp.data };
132
+ } catch (err: any) {
133
+ return { error: true, message: err?.message ?? String(err) };
134
+ }
135
+ }
136
+
137
+ /**
138
+ * garbageCollectionCache removes files from the cache directory that exceed the specified maximum file size in megabytes.
139
+ *
140
+ * @public
141
+ * @static
142
+ * @param {number} maxFileMegabytes
143
+ */
144
+ public static garbageCollectionCache(maxFileMegabytes: number): void {
145
+ try {
146
+ const settings = loader.settings as types.ClientSettings;
147
+ if (!settings.NoaaWeatherWireService.cache.directory) return;
148
+ if (!loader.packages.fs.existsSync(settings.NoaaWeatherWireService.cache.directory)) return;
149
+ const maxBytes = maxFileMegabytes * 1024 * 1024;
150
+ const cacheDirectory = settings.NoaaWeatherWireService.cache.directory;
151
+ const stackFiles: string[] = [cacheDirectory], files: {
152
+ file: string,
153
+ size: number,
154
+ }[] = [];
155
+ while (stackFiles.length) {
156
+ const currentDirectory = stackFiles.pop();
157
+ loader.packages.fs.readdirSync(currentDirectory).forEach((file: string) => {
158
+ const fullPath = loader.packages.path.join(currentDirectory, file);
159
+ const stat = loader.packages.fs.statSync(fullPath)
160
+ if (stat.isDirectory()) stackFiles.push(fullPath) ;
161
+ else files.push({ file: fullPath, size: stat.size });
162
+ })
163
+ }
164
+ if (!files.length) return;
165
+ files.forEach(f => {
166
+ if (f.size > maxBytes) loader.packages.fs.unlinkSync(f.file);
167
+ })
168
+ } catch (error: any) {
169
+ loader.cache.events.emit('onError', { code: 'error-garbage-collection', message: `Failed to perform garbage collection: ${error.message}`});
170
+ }
171
+ }
172
+
173
+ /**
174
+ * handleCronJob performs periodic tasks based on whether the client is connected to NWWS or fetching data from NWS.
175
+ *
176
+ * @public
177
+ * @static
178
+ * @param {boolean} isNwws
179
+ */
180
+ public static handleCronJob(isNwws: boolean): void {
181
+ try {
182
+ const settings = loader.settings as types.ClientSettings;
183
+ if (isNwws) {
184
+ if (settings.NoaaWeatherWireService.cache.read ) void this.garbageCollectionCache(settings.NoaaWeatherWireService.cache.maxSizeMB);
185
+ if (settings.NoaaWeatherWireService.clientReconnections.canReconnect ) void Xmpp.isSessionReconnectionEligible(settings.NoaaWeatherWireService.clientReconnections.currentInterval);
186
+ } else {
187
+ void this.loadGeoJsonData();
188
+ }
189
+ } catch (error: any) {
190
+ loader.cache.events.emit('onError', { code: 'error-cron-job', message: `Failed to perform scheduled tasks: ${error.message}`});
191
+ }
192
+ }
193
+
194
+ /**
195
+ * mergeClientSettings merges user-provided settings into the existing client settings, allowing for nested objects to be merged correctly.
196
+ *
197
+ * @public
198
+ * @static
199
+ * @param {Record<string, any>} target
200
+ * @param {Record<string, any>} settings
201
+ */
202
+ public static mergeClientSettings(target: Record<string, any>, settings: Record<string, any>): void {
203
+ for (const key in settings) {
204
+ if (settings.hasOwnProperty(key)) {
205
+ if (typeof settings[key] === 'object' && settings[key] !== null && !Array.isArray(settings[key])) {
206
+ if (!target[key] || typeof target[key] !== 'object') { target[key] = {}; }
207
+ this.mergeClientSettings(target[key], settings[key]);
208
+ } else {
209
+ target[key] = settings[key];
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+
216
+ export default Utils;
package/src/xmpp.ts ADDED
@@ -0,0 +1,123 @@
1
+ /*
2
+ _ _ __ __
3
+ /\ | | | | (_) \ \ / /
4
+ / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V /
5
+ / /\ \| __| "_ ` _ \ / _ \/ __| "_ \| "_ \ / _ \ "__| |/ __| > <
6
+ / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \
7
+ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\
8
+ | |
9
+ |_|
10
+
11
+ Written by: KiyoWx (k3yomi)
12
+ */
13
+
14
+
15
+ import * as loader from './bootstrap';
16
+ import * as types from './types';
17
+ import Utils from './utils';
18
+ import StanzaParser from './parsers/stanza';
19
+ import Database from './database';
20
+ import EventParser from './parsers/events';
21
+
22
+
23
+ export class Xmpp {
24
+
25
+ /**
26
+ * isSessionReconnectionEligible checks if the XMPP session is eligible for reconnection based on the last
27
+ * received stanza time and current interval.
28
+ *
29
+ * @public
30
+ * @static
31
+ * @async
32
+ * @param {number} currentInterval
33
+ * @returns {Promise<void>}
34
+ */
35
+ public static async isSessionReconnectionEligible(currentInterval: number): Promise<void> {
36
+ const settings = loader.settings as types.ClientSettings;
37
+ if ((loader.cache.isConnected || loader.cache.sigHalt ) && loader.cache.session) {
38
+ const lastStanza = Date.now() - loader.cache.lastStanza;
39
+ if (lastStanza >= (currentInterval * 1000)) {
40
+ if (!loader.cache.attemptingReconnect) {
41
+ loader.cache.attemptingReconnect = true;
42
+ loader.cache.isConnected = false;
43
+ loader.cache.totalReconnects += 1;
44
+ loader.cache.events.emit(`onReconnect`, { reconnects: loader.cache.totalReconnects, lastStanza: lastStanza, lastName: settings.NoaaWeatherWireService.clientCredentials.nickname})
45
+ await loader.cache.session.stop().catch(() => {});
46
+ await loader.cache.session.start().catch(() => {});
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * deploySession initializes and starts the XMPP client session, setting up event listeners for
54
+ * connection management and message handling. This function is specifically tailored for
55
+ * NoaaWeatherWireService and connects to their XMPP server.
56
+ *
57
+ * @public
58
+ * @static
59
+ * @async
60
+ * @returns {Promise<void>}
61
+ */
62
+ public static async deploySession(): Promise<void> {
63
+ const settings = loader.settings as types.ClientSettings
64
+ loader.cache.session = loader.packages.xmpp.client({
65
+ service: `xmpp://nwws-oi.weather.gov`,
66
+ domain: `nwws-oi.weather.gov`,
67
+ username: settings.NoaaWeatherWireService.clientCredentials.username,
68
+ password: settings.NoaaWeatherWireService.clientCredentials.password,
69
+ });
70
+ settings.NoaaWeatherWireService.clientCredentials.nickname ??= settings.NoaaWeatherWireService.clientCredentials.username;
71
+ loader.cache.session.on(`online`, async (address: string) => {
72
+ if (loader.cache.lastConnect && Date.now() - loader.cache.lastConnect < 10 * 1000) {
73
+ loader.cache.sigHalt = true;
74
+ Utils.sleep(2 * 1000).then(async () => {
75
+ await loader.cache.session.stop()
76
+ });
77
+ loader.cache.events.emit(`onError`, { code: `error-reconnecting-too-fast`, message: `The client is attempting to reconnect too fast. Please wait a few seconds before trying again.` });
78
+ return;
79
+ }
80
+ loader.cache.isConnected = true;
81
+ loader.cache.sigHalt = false;
82
+ loader.cache.lastConnect = Date.now();
83
+ loader.cache.session.send(loader.packages.xmpp.xml('presence', { to: `nwws@conference.nwws-oi.weather.gov/${settings.NoaaWeatherWireService.clientCredentials.nickname}`, xmlns: 'http://jabber.org/protocol/muc' }))
84
+ loader.cache.session.send(loader.packages.xmpp.xml('presence', { to: `nwws@conference.nwws-oi.weather.gov`, type: 'available' }))
85
+ loader.cache.events.emit(`onConnection`, settings.NoaaWeatherWireService.clientCredentials.nickname)
86
+ if (loader.cache.attemptingReconnect) {
87
+ Utils.sleep(15 * 1000).then(() => { loader.cache.attemptingReconnect = false; })
88
+ }
89
+ });
90
+ loader.cache.session.on(`offline`, async () => {
91
+ loader.cache.isConnected = false;
92
+ loader.cache.sigHalt = true;
93
+ loader.cache.events.emit(`onError`, { code: `connection-lost`, message: `XMPP connection went offline` });
94
+ });
95
+ loader.cache.session.on(`error`, async (error: Error) => {
96
+ loader.cache.isConnected = false;
97
+ loader.cache.sigHalt = true;
98
+ loader.cache.events.emit(`onError`, { code: `connection-error`, message: error.message });
99
+ });
100
+ loader.cache.session.on(`stanza`, async (stanza: any) => {
101
+ try {
102
+ loader.cache.lastStanza = Date.now()
103
+ if (stanza.is(`message`)) {
104
+ const validate = StanzaParser.validate(stanza);
105
+ if ( validate.ignore || (validate.isCap && !settings.NoaaWeatherWireService.alertPreferences.isCapOnly) || (!validate.isCap && settings.NoaaWeatherWireService.alertPreferences.isCapOnly) || (validate.isCap && !validate.isCapDescription) ) return;
106
+ EventParser.eventHandler(validate);
107
+ loader.cache.events.emit(`onMessage`, validate)
108
+ Database.stanzaCacheImport(JSON.stringify(validate))
109
+ }
110
+ if (stanza.is(`presence`) && stanza.attrs.from && stanza.attrs.from.startsWith('nwws@conference.nwws-oi.weather.gov/')) {
111
+ const occupant = stanza.attrs.from.split('/').slice(1).join('/');
112
+ loader.cache.events.emit('onOccupant', { occupant, type: stanza.attrs.type === 'unavailable' ? 'unavailable' : 'available' });
113
+ }
114
+ } catch (e) {
115
+ loader.cache.events.emit(`onError`, {code: `error-processing-stanza`, message: (e as Error).message})
116
+ }
117
+ });
118
+ await loader.cache.session.start()
119
+ }
120
+
121
+ }
122
+
123
+ export default Xmpp;
package/test.js ADDED
@@ -0,0 +1 @@
1
+ // TODO:
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES6",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "outDir": "dist",
7
+ "declaration": true,
8
+ "declarationDir": "dist/types",
9
+ "esModuleInterop": true,
10
+ "allowSyntheticDefaultImports": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src"]
14
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/**/*.ts'],
5
+ format: ['esm', 'cjs'],
6
+ outDir: 'dist',
7
+ splitting: false,
8
+ clean: true,
9
+ outExtension({ format }) {return {js: format === 'esm' ? '.mjs' : '.cjs',};},
10
+ esbuildOptions(options, context) { options.outdir = `dist/${context.format}`; },
11
+ });