@atmosx/event-product-parser 3.0.46 → 3.0.48

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/dist/index.d.mts CHANGED
@@ -10,6 +10,7 @@ type TypeListener = {
10
10
  uploads?: {
11
11
  file?: boolean;
12
12
  eas?: boolean;
13
+ event?: boolean;
13
14
  };
14
15
  };
15
16
 
@@ -42,6 +43,11 @@ type TypeSettings = {
42
43
  CallbackInterval: number;
43
44
  EventsEndpoint: string;
44
45
  };
46
+ BroadcastifySettings: {
47
+ BroadcastifyAttachments: boolean;
48
+ BroadcastifyDatabase: string;
49
+ BroadcastifyTags: string[];
50
+ };
45
51
  GlobalSettings: {
46
52
  EventManagement: boolean;
47
53
  BetterEventNames: boolean;
@@ -64,6 +70,7 @@ type TypeSettings = {
64
70
  };
65
71
  ArchiveSettings: {
66
72
  TTL: number;
73
+ EventDirectory: string;
67
74
  TextDirectory: string;
68
75
  EasDirectory: string;
69
76
  EasToneout: string;
@@ -84,6 +91,7 @@ type TypeAttributes = {
84
91
 
85
92
  type TypeEventProperties = {
86
93
  locations: string;
94
+ locations_array: string[];
87
95
  description: string;
88
96
  attributes?: TypeAttributes;
89
97
  geocode: {
@@ -200,7 +208,10 @@ type TypeEvent = {
200
208
  issued: string;
201
209
  status: string;
202
210
  }[];
203
- attachments?: string[];
211
+ attachments?: {
212
+ name: string;
213
+ link: string;
214
+ }[];
204
215
  raw: string;
205
216
  };
206
217
  } & TypeEventProperties;
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ type TypeListener = {
10
10
  uploads?: {
11
11
  file?: boolean;
12
12
  eas?: boolean;
13
+ event?: boolean;
13
14
  };
14
15
  };
15
16
 
@@ -42,6 +43,11 @@ type TypeSettings = {
42
43
  CallbackInterval: number;
43
44
  EventsEndpoint: string;
44
45
  };
46
+ BroadcastifySettings: {
47
+ BroadcastifyAttachments: boolean;
48
+ BroadcastifyDatabase: string;
49
+ BroadcastifyTags: string[];
50
+ };
45
51
  GlobalSettings: {
46
52
  EventManagement: boolean;
47
53
  BetterEventNames: boolean;
@@ -64,6 +70,7 @@ type TypeSettings = {
64
70
  };
65
71
  ArchiveSettings: {
66
72
  TTL: number;
73
+ EventDirectory: string;
67
74
  TextDirectory: string;
68
75
  EasDirectory: string;
69
76
  EasToneout: string;
@@ -84,6 +91,7 @@ type TypeAttributes = {
84
91
 
85
92
  type TypeEventProperties = {
86
93
  locations: string;
94
+ locations_array: string[];
87
95
  description: string;
88
96
  attributes?: TypeAttributes;
89
97
  geocode: {
@@ -200,7 +208,10 @@ type TypeEvent = {
200
208
  issued: string;
201
209
  status: string;
202
210
  }[];
203
- attachments?: string[];
211
+ attachments?: {
212
+ name: string;
213
+ link: string;
214
+ }[];
204
215
  raw: string;
205
216
  };
206
217
  } & TypeEventProperties;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@atmosx/event-product-parser",
3
- "version": "3.0.46",
3
+ "version": "3.0.48",
4
4
  "description": "NOAA Weather Wire & NWS API Parser - Built for standalone and Project AtmosphericX Integration.",
5
5
  "main": "dist/cjs/index.cjs",
6
6
  "module": "dist/esm/index.mjs",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/AtmosphericX/event-product-parser",
10
- "branch": "beta"
10
+ "branch": "v3-beta-branch"
11
11
  },
12
12
  "scripts": {
13
13
  "start": "cd ./test && node ./index.js",
@@ -33,14 +33,13 @@
33
33
  },
34
34
  "homepage": "https://github.com/AtmosphericX/event-product-parser#readme",
35
35
  "dependencies": {
36
- "express-ws": "5.0.2",
37
- "express": "5.2.1",
38
36
  "@xmpp/client": "0.14.0",
39
37
  "better-sqlite3": "12.9.0",
40
38
  "croner": "10.0.1",
39
+ "express": "5.2.1",
40
+ "express-ws": "5.0.2",
41
41
  "jszip": "3.10.1",
42
42
  "polygon-clipping": "0.15.7",
43
- "request": "2.88.2",
44
43
  "say": "0.16.0",
45
44
  "shapefile": "0.6.6",
46
45
  "typescript": "5.9.3",
@@ -17,15 +17,33 @@
17
17
 
18
18
  */
19
19
 
20
+ import { bootstrap } from "../bootstrap"
20
21
  import { TypeEvent } from "../@types/type.event"
21
22
  import { getLatestIssuance } from "../@modules/@utilities/utilities.getLatestIssuance";
23
+ import { dict_states } from "../@dictionaries/dictionaries.states"
24
+ import { dict_expressions } from "../@dictionaries/dictionaries.expressions"
22
25
 
23
- export const getEventAttachments = (event: TypeEvent): string[] | null => {
24
- const latestTime = getLatestIssuance();
26
+ interface GetEventAttachmentsResponse {
27
+ name: string
28
+ link: string
29
+ }
30
+
31
+ type BroadcastifyResponse = {
32
+ state: string
33
+ county: string
34
+ feed: string
35
+ type: string
36
+ link: string
37
+ }
38
+
39
+ export const getEventAttachments = (event: TypeEvent): GetEventAttachmentsResponse[] | null => {
40
+ let attachments = [];
41
+ const issuanceTime = getLatestIssuance();
25
42
  const spcNumber = event?.properties?.discussion_parameters?.discussion_number;
26
43
  const watchNumber = event?.properties?.watch_parameters?.watch_number;
27
- const validEvents = [
28
- { target: "Day 1", attachment: `https://www.spc.noaa.gov/products/outlook/day1otlk_${latestTime}.png` },
44
+ const locations = event?.properties?.locations_array;
45
+ const events = [
46
+ { target: "Day 1", attachment: `https://www.spc.noaa.gov/products/outlook/day1otlk_${issuanceTime}.png` },
29
47
  { target: "Day 2", attachment: `https://www.spc.noaa.gov/products/outlook/day2otlk.png` },
30
48
  { target: "Day 3", attachment: `https://www.spc.noaa.gov/products/outlook/day3otlk.png` },
31
49
  { target: "Mesoscale Discussion", attachment: `https://www.spc.noaa.gov/products/md/mcd${spcNumber}.png` },
@@ -34,9 +52,32 @@ export const getEventAttachments = (event: TypeEvent): string[] | null => {
34
52
  { target: "Severe Thunderstorm Watch", attachment: `https://www.spc.noaa.gov/products/watch/ww${watchNumber}_radar_big.gif` },
35
53
  { target: "PDS Severe Thunderstorm Watch", attachment: `https://www.spc.noaa.gov/products/watch/ww${watchNumber}_radar_big.gif` }
36
54
  ]
37
- const isValid = validEvents.find(outlook => outlook.target == event.properties.event);
38
- if (isValid) {
39
- return isValid?.attachment ? [isValid.attachment] : [];
55
+
56
+ if (events.find((e) => e.target === event.properties.event)) {
57
+ attachments.push({ name: `Image: Graphic`, link: events.find((e) => e.target === event.properties.event).attachment });
58
+ }
59
+
60
+ for (const location of locations) {
61
+ if (dict_expressions.location.test(location)) {
62
+ const lines = [`Northern`, `Southern`, `Eastern`, `Western`, `Inland`, `Costal`, `County`]
63
+ const county = location?.split(',')[0]?.trim().replace(new RegExp(`^(${lines.join('|')}) `), '')
64
+ const state = dict_states[location?.split(',')[1]?.trim()]
65
+ const feeds = bootstrap.database.prepare(`SELECT * FROM broadcastify WHERE state = ? AND county = ?`).all(state, county).sort((a, b) => {
66
+ const typeOrder = ['Other', 'Public Safety'];
67
+ const indexA = typeOrder.indexOf(a.type);
68
+ const indexB = typeOrder.indexOf(b.type);
69
+ return indexA - indexB;
70
+ });
71
+ const tags = bootstrap.settings.BroadcastifySettings.BroadcastifyTags;
72
+ const filtered = feeds.filter((feed: BroadcastifyResponse) => tags.includes(feed.type));
73
+ if (filtered.length > 0) {
74
+ filtered.map((filtered: BroadcastifyResponse) => {
75
+ if (!attachments.some((a) => a.link === filtered.link)) {
76
+ attachments.push({ name: `${filtered.type}: ${filtered.feed}`, link: filtered.link });
77
+ }
78
+ });
79
+ }
80
+ }
40
81
  }
41
- return null;
82
+ return attachments;
42
83
  }
@@ -42,6 +42,7 @@ export const properties = (options: GetPropertiesOptions): TypeEventProperties =
42
42
  const polygons = getPolygonFromProduct(options.message)
43
43
  const properties = {
44
44
  locations: options?.ugc?.locations?.join(`; `) ?? null,
45
+ locations_array: options?.ugc?.locations ?? [],
45
46
  description: getDescriptionFromProduct({ message: options.message, handle: options?.pVtec?.vtec ?? null }),
46
47
  attributes: options.attributes,
47
48
  geocode: {
@@ -20,7 +20,7 @@
20
20
  import { TypeEvent } from "../@types/type.event";
21
21
  import { dict_correlations } from "../@dictionaries/dictionaries.correlations";
22
22
  import { dict_cancellation } from "../@dictionaries/dictionaries.cancellation";
23
- import { dict_testing} from "../@dictionaries/dictionaries.test"
23
+ import { dict_testing} from "../@dictionaries/dictionaries.testing"
24
24
  import { dict_products } from "../@dictionaries/dictionaries.products";
25
25
  import { dict_hail } from "../@dictionaries/dictionaries.hail"
26
26
 
@@ -45,6 +45,7 @@ export const createEvent = (options: CreateEventOptions): void => {
45
45
  issued: options.issued.toISOString(),
46
46
  expires: options.expires.toISOString(),
47
47
  locations: options.locations,
48
+ locations_array: options.locations ? options.locations.split(';') : [],
48
49
  description: options.description,
49
50
  geocode: {
50
51
  office: {
@@ -21,6 +21,7 @@ export const dict_cancellation: string[] = [
21
21
  "has been cancelled",
22
22
  "subsided sufficiently for the advisory to be cancelled",
23
23
  "has been cancelled",
24
+ "no tsunami threat from a nearby earthquake",
24
25
  "will be allowed to expire",
25
26
  "has diminished",
26
27
  "and no longer",
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  export const dict_expressions: { [key: string]: RegExp } = {
21
+ location: new RegExp(/^([A-Za-z.\-' ]+),\s*([A-Z]{2})$/),
21
22
  pvtec: new RegExp(`[OTEX].(NEW|CON|EXT|EXA|EXB|UPG|CAN|EXP|COR|ROU).[A-Z]{4}.[A-Z]{2}.[WAYSFON].[0-9]{4}.[0-9]{6}T[0-9]{4}Z-[0-9]{6}T[0-9]{4}Z`, "g"),
22
23
  hvtec: new RegExp(`[a-zA-Z0-9]{4}.[A-Z0-9].[A-Z]{2}.[0-9]{6}T[0-9]{4}Z.[0-9]{6}T[0-9]{4}Z.[0-9]{6}T[0-9]{4}Z.[A-Z]{2}`, "imu"),
23
24
  wmo: new RegExp(`[A-Z0-9]{6}\\s[A-Z]{4}\\s\\d{6}`, "imu"),
@@ -0,0 +1,72 @@
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
+ export const dict_states: Record<string, string> = {
21
+ "AL": "Alabama",
22
+ "AK": "Alaska",
23
+ "AZ": "Arizona",
24
+ "AR": "Arkansas",
25
+ "CA": "California",
26
+ "CO": "Colorado",
27
+ "CT": "Connecticut",
28
+ "DE": "Delaware",
29
+ "FL": "Florida",
30
+ "GA": "Georgia",
31
+ "HI": "Hawaii",
32
+ "ID": "Idaho",
33
+ "IL": "Illinois",
34
+ "IN": "Indiana",
35
+ "IA": "Iowa",
36
+ "KS": "Kansas",
37
+ "KY": "Kentucky",
38
+ "LA": "Louisiana",
39
+ "ME": "Maine",
40
+ "MD": "Maryland",
41
+ "MA": "Massachusetts",
42
+ "MI": "Michigan",
43
+ "MN": "Minnesota",
44
+ "MS": "Mississippi",
45
+ "MO": "Missouri",
46
+ "MT": "Montana",
47
+ "NE": "Nebraska",
48
+ "NV": "Nevada",
49
+ "NH": "New Hampshire",
50
+ "NJ": "New Jersey",
51
+ "NM": "New Mexico",
52
+ "NY": "New York",
53
+ "NC": "North Carolina",
54
+ "ND": "North Dakota",
55
+ "OH": "Ohio",
56
+ "OK": "Oklahoma",
57
+ "OR": "Oregon",
58
+ "PA": "Pennsylvania",
59
+ "RI": "Rhode Island",
60
+ "SC": "South Carolina",
61
+ "SD": "South Dakota",
62
+ "TN": "Tennessee",
63
+ "TX": "Texas",
64
+ "UT": "Utah",
65
+ "VT": "Vermont",
66
+ "VA": "Virginia",
67
+ "WA": "Washington",
68
+ "WV": "West Virginia",
69
+ "WI": "Wisconsin",
70
+ "WY": "Wyoming",
71
+ "DC": "District of Columbia"
72
+ };
@@ -47,6 +47,7 @@ export const api = async (stanza: TypeStanzaCompiled): Promise<void> => {
47
47
  issued: feature?.properties?.sent ? new Date(feature?.properties?.sent).toISOString() : null,
48
48
  expires: feature?.properties?.expires ? new Date(feature?.properties?.expires).toISOString() : null,
49
49
  locations: feature?.properties?.areaDesc ?? null,
50
+ locations_array: feature?.properties?.areaDesc ? feature?.properties?.areaDesc.split(';') : [],
50
51
  description: feature?.properties?.description ?? null,
51
52
  attributes: feature?.properties?.attributes ?? {},
52
53
  geocode: {
@@ -24,9 +24,10 @@ import { bootstrap } from "../bootstrap"
24
24
  import { setEasTone } from "../@modules/@eas/eas.setEasTone"
25
25
  import { setTimeoutAction } from "../@modules/@utilities/utilities.setTimeoutAction";
26
26
  import { getEmebed } from "../@parsers/@text/text.getEmbed";
27
+ import { getCleanedEvent } from "../@building/building.clean"
27
28
  import { createHttp } from "../@modules/@utilities/utilities.createHttp";
28
29
  import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from "fs";
29
- import FormData from "form-data";
30
+ import { setWarning } from "../@modules/@utilities/utilities.setWarning";
30
31
 
31
32
 
32
33
 
@@ -35,90 +36,125 @@ export const updateListener = async (event: TypeEvent): Promise<void> => {
35
36
  const settings = bootstrap.settings;
36
37
  const listeners = settings.ListenerSettings as TypeListener[];
37
38
  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
-
39
+ const metadata = {
40
+ file: null,
41
+ eas: null,
42
+ name: properties.event,
43
+ status: properties.status,
44
+ description: properties.description,
45
+ tracking: properties.metadata.tracking,
46
+ header: properties.metadata.header,
47
+ raw: properties.metadata.raw,
48
+ expired: properties.status_metadata.is_expired,
49
+ attachments: properties.metadata.attachments
50
+ }
43
51
 
44
- const matched = events.some(pattern => {
52
+ for (const listener of listeners) {
53
+ const events = listener?.events;
54
+ const webhook = listener?.webhook;
55
+ const uploads = listener?.uploads
56
+ const isMatched = events.some(pattern => {
45
57
  if (!pattern) return false;
46
- if (pattern === "*" || pattern === properties.event) return true;
58
+ if (pattern === "*" || pattern === metadata.name) return true;
47
59
  if (pattern.includes("*")) {
48
60
  const regex = "^" +
49
61
  pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$";
50
- return new RegExp(regex).test(properties.event);
62
+ return new RegExp(regex).test(metadata.name);
51
63
  }
52
64
  return false;
53
65
  });
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
66
+
67
+ if (events?.length == 0 || isMatched) {
68
+ if (uploads?.eas) {
69
+ metadata.eas = await setEasTone({
70
+ title: `${metadata.name}_${metadata.status}_${metadata.tracking}`,
71
+ message: metadata.description,
72
+ header: metadata.header
63
73
  });
64
74
  }
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);
75
+
76
+ if (uploads?.file) {
77
+ const fDestination = settings.GlobalSettings.ArchiveSettings.TextDirectory;
78
+ if (fDestination) {
79
+ if (!existsSync(fDestination)) { mkdirSync(fDestination, { recursive: true }); }
80
+ if (existsSync(`${fDestination}/${metadata.name}_${metadata.status}_${metadata.tracking}.txt`)) {
81
+ appendFileSync(`${fDestination}/${metadata.name}_${metadata.status}_${metadata.tracking}.txt`, metadata.raw);
71
82
  } else {
72
- writeFileSync(`${destination}/${properties.event}_${properties.status}_${properties.metadata.tracking}.txt`, properties.metadata.raw);
83
+ writeFileSync(`${fDestination}/${metadata.name}_${metadata.status}_${metadata.tracking}.txt`, metadata.raw);
73
84
  }
74
85
  }
75
- file = properties.metadata.raw;
86
+ metadata.file = metadata.raw;
76
87
  }
77
88
 
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
- }
89
+ if (uploads?.event) {
90
+ const eDestination = settings.GlobalSettings.ArchiveSettings.EventDirectory;
91
+ if (eDestination) {
92
+ if (!existsSync(eDestination)) { mkdirSync(eDestination, { recursive: true }); }
93
+ writeFileSync(`${eDestination}/${metadata.name}_${metadata.status}_${metadata.tracking}.json`, JSON.stringify(getCleanedEvent(event), null, 2));
94
+ }
95
+ }
96
+
97
+ if (webhook.enabled && webhook.destination) {
98
+ const ratelimit = setTimeoutAction({ identifier: webhook.destination, interval: webhook.ratelimit * 2, max: webhook.ratelimit, addTime: true })
99
+ const form = new FormData();
100
+ const embed = {
101
+ title: `${metadata.name} (${metadata.status})`,
102
+ description: getEmebed(event),
103
+ fields: [],
104
+ color: 16711680,
105
+ timestamp: new Date().toISOString(),
106
+ footer: { text: webhook.title ?? `AtmosphericX` }
107
+ };
108
+
109
+ if (ratelimit.limited) { return }
87
110
 
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" });
111
+
112
+ if (metadata.description && !metadata.expired) {
113
+ if (metadata.description.length > 900) {
114
+ metadata.description = metadata.description.substring(0, 900) + "\n\n[Message truncated due to length]";
106
115
  }
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
- }
116
+ embed.fields.push({
117
+ name: "Description",
118
+ value: metadata.description ? '```' + '\n' + metadata.description.split('\n').map(l => l.trim()).filter(Boolean).join('\n') + '\n' + '```' : ""
119
+ });
120
+ }
121
+
122
+
123
+ if (metadata.attachments?.length > 0) {
124
+ metadata.attachments = metadata.attachments.slice(0, 5);
125
+ embed.fields.push({
126
+ name: "Attachments",
127
+ value: metadata.attachments.map(attachment => `- [${attachment.name.length > 45 ? attachment.name.substring(0, 45) + '...' : attachment.name}](${attachment.link})`).join('\n')
128
+ });
129
+ }
130
+
131
+ if (uploads?.file) {
132
+ form.append("fUpload", new Blob([Buffer.from(metadata.file)], {type: "application/text"}), `${properties.event}_${properties.status}_${properties.metadata.tracking}.txt`)
133
+ }
134
+
135
+ if (uploads?.eas) {
136
+ if (metadata.eas) {
137
+ const file = readFileSync(metadata.eas)
138
+ form.append("fEas", new Blob([Buffer.from(file)], { type: "audio/mpeg" }), `${properties.event}_${properties.status}_${properties.metadata.tracking}_eas.mp3`)
112
139
  }
113
- const a = await createHttp({
114
- url: sWebhook.destination,
115
- timeout: 5000,
116
- method: `POST`,
117
- headers: form.getHeaders(),
118
- body: form
119
- })
140
+ }
141
+
142
+ form.append("payload_json", JSON.stringify({
143
+ username: webhook.title ?? "AtmosphericX",
144
+ content: webhook.message ?? "",
145
+ embeds: [embed]
146
+ }));
147
+
148
+ const a = await createHttp({
149
+ url: webhook.destination,
150
+ timeout: 5000,
151
+ method: `POST`,
152
+ body: form
153
+ })
154
+ if (a.error) {
155
+ setWarning({ message: `Failed to send webhook: ${a.error.message}` })
120
156
  }
121
157
  }
122
158
  }
123
159
  }
124
- };
160
+ }
@@ -0,0 +1,67 @@
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/utilities.createHttp'
22
+ import { setWarning } from "../@utilities/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
+ };
@@ -23,6 +23,7 @@ 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'
26
+ import { importBroadcastify } from "./database.broadcastify"
26
27
 
27
28
  export const initializeDatabase = async (): Promise<void> => {
28
29
  const settings = bootstrap.settings as TypeSettings;
@@ -38,7 +39,19 @@ export const initializeDatabase = async (): Promise<void> => {
38
39
  bootstrap.database
39
40
  .prepare(`CREATE TABLE IF NOT EXISTS shapefiles (id TEXT PRIMARY KEY, location TEXT, geometry TEXT)`)
40
41
  .run();
41
- await importShapefiles();
42
+ const isNeedingShapefiles = bootstrap.database
43
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='shapefiles';`)
44
+ .get();
45
+ const isNeedingBroadcastify = bootstrap.database
46
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='broadcastify';`)
47
+ .get();
48
+
49
+ if (!isNeedingShapefiles || !isNeedingBroadcastify) {
50
+ 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.` })
51
+ await importBroadcastify();
52
+ await importShapefiles();
53
+ setWarning({ message: `Building has completed, you can now continue or close the terminal`})
54
+ }
42
55
  } catch (error) {
43
56
  setWarning({ message: `An error occurred while initializing the database: ${error.message}` })
44
57
  }