Compare commits
40 commits
sharding-t
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
664e00b7f4 | ||
|
cce9b12645 | ||
|
c82ab68c1e | ||
|
a543386394 | ||
|
1ae72ac979 | ||
|
1a7c1735f4 | ||
|
5b487125aa | ||
|
770813b4a7 | ||
|
ded7a2373c | ||
|
79ae2f4c25 | ||
|
e02faaab18 | ||
|
bd275403e9 | ||
|
9b80f79436 | ||
|
4bb679cef8 | ||
|
e952ec60e7 | ||
|
0227979176 | ||
|
9f31d18a6f | ||
|
d491fda5c8 | ||
|
96912d325a | ||
|
00f97fd6e6 | ||
|
09f3fc957c | ||
|
cbb1842100 | ||
|
f763a95472 | ||
|
d88188182c | ||
|
8b94e7e3f8 | ||
|
d653bc3e90 | ||
|
b5bf06a38d | ||
|
ae63a0d52d | ||
|
73e42e2288 | ||
|
9afdd5626e | ||
|
f6acab4d20 | ||
|
b10308ef36 | ||
|
aa8fe9498f | ||
|
d48fe3085a | ||
|
662e0e775c | ||
|
845fead37a | ||
|
70c30420fc | ||
|
98673be5c7 | ||
|
aac1420964 | ||
|
7a12ed05d3 |
1
.vscode/settings.json
vendored
Normal file
1
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -91,6 +91,4 @@
|
||||||
"zsechat@conference.weather.im",
|
"zsechat@conference.weather.im",
|
||||||
"zdcchat@conference.weather.im",
|
"zdcchat@conference.weather.im",
|
||||||
"znychat@conference.weather.im"
|
"znychat@conference.weather.im"
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
|
@ -217,7 +217,7 @@
|
||||||
},
|
},
|
||||||
"DSW": {
|
"DSW": {
|
||||||
"text": "Dust Storm Warning",
|
"text": "Dust Storm Warning",
|
||||||
"priority": 5
|
"priority": 4
|
||||||
},
|
},
|
||||||
"EFP": {
|
"EFP": {
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
|
@ -365,7 +365,7 @@
|
||||||
},
|
},
|
||||||
"FFW": {
|
"FFW": {
|
||||||
"text": "Flash Flood Warning",
|
"text": "Flash Flood Warning",
|
||||||
"priority": 4
|
"priority": 5
|
||||||
},
|
},
|
||||||
"FLN": {
|
"FLN": {
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
|
@ -385,7 +385,7 @@
|
||||||
},
|
},
|
||||||
"FRW": {
|
"FRW": {
|
||||||
"text": "Fire Warning",
|
"text": "Fire Warning",
|
||||||
"priority": 4
|
"priority": 3
|
||||||
},
|
},
|
||||||
"FSH": {
|
"FSH": {
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
|
@ -680,7 +680,7 @@
|
||||||
"text": "Data Mgt Message"
|
"text": "Data Mgt Message"
|
||||||
},
|
},
|
||||||
"NPW": {
|
"NPW": {
|
||||||
"priority": 1,
|
"priority": 3,
|
||||||
"text": "Non-Precipitation Warnings / Watches / Advisories"
|
"text": "Non-Precipitation Warnings / Watches / Advisories"
|
||||||
},
|
},
|
||||||
"NSH": {
|
"NSH": {
|
||||||
|
@ -1160,7 +1160,7 @@
|
||||||
"text": "Tropical Cyclone Update"
|
"text": "Tropical Cyclone Update"
|
||||||
},
|
},
|
||||||
"TCV": {
|
"TCV": {
|
||||||
"priority": 1,
|
"priority": 4,
|
||||||
"text": "Tropical Cyclone Watch/Warning Break Points"
|
"text": "Tropical Cyclone Watch/Warning Break Points"
|
||||||
},
|
},
|
||||||
"TIB": {
|
"TIB": {
|
||||||
|
@ -1308,7 +1308,7 @@
|
||||||
"text": "Routine Space Environment Product Issued Weekly"
|
"text": "Routine Space Environment Product Issued Weekly"
|
||||||
},
|
},
|
||||||
"WOU": {
|
"WOU": {
|
||||||
"priority": 4,
|
"priority": 5,
|
||||||
"text": "Tornado/Severe Thunderstorm Watch"
|
"text": "Tornado/Severe Thunderstorm Watch"
|
||||||
},
|
},
|
||||||
"WS1": {
|
"WS1": {
|
||||||
|
@ -1345,7 +1345,7 @@
|
||||||
},
|
},
|
||||||
"WSW": {
|
"WSW": {
|
||||||
"text": "Winter Storm Warning",
|
"text": "Winter Storm Warning",
|
||||||
"priority": 5
|
"priority": 4
|
||||||
},
|
},
|
||||||
"WWA": {
|
"WWA": {
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
|
@ -1365,11 +1365,11 @@
|
||||||
},
|
},
|
||||||
"CFA": {
|
"CFA": {
|
||||||
"text": "Coastal Flood Watch",
|
"text": "Coastal Flood Watch",
|
||||||
"priority": 4
|
"priority": 3
|
||||||
},
|
},
|
||||||
"FLA": {
|
"FLA": {
|
||||||
"text": "Flood Watch",
|
"text": "Flood Watch",
|
||||||
"priority": 2
|
"priority": 3
|
||||||
},
|
},
|
||||||
"HWA": {
|
"HWA": {
|
||||||
"text": "High Wind Watch",
|
"text": "High Wind Watch",
|
||||||
|
@ -1389,7 +1389,7 @@
|
||||||
},
|
},
|
||||||
"SVA": {
|
"SVA": {
|
||||||
"text": "Severe Thunderstorm Watch",
|
"text": "Severe Thunderstorm Watch",
|
||||||
"priority": 4
|
"priority": 5
|
||||||
},
|
},
|
||||||
"TOA": {
|
"TOA": {
|
||||||
"text": "Tornado Watch",
|
"text": "Tornado Watch",
|
||||||
|
@ -1405,7 +1405,7 @@
|
||||||
},
|
},
|
||||||
"TSA": {
|
"TSA": {
|
||||||
"text": "Tsunami Watch",
|
"text": "Tsunami Watch",
|
||||||
"priority": 4
|
"priority": 5
|
||||||
},
|
},
|
||||||
"TSW": {
|
"TSW": {
|
||||||
"text": "Tsunami Warning",
|
"text": "Tsunami Warning",
|
||||||
|
@ -1522,6 +1522,9 @@
|
||||||
"REP": {
|
"REP": {
|
||||||
"text": "RECCO Observations (tropical cyclone)",
|
"text": "RECCO Observations (tropical cyclone)",
|
||||||
"priority": 3
|
"priority": 3
|
||||||
|
},
|
||||||
|
"PIR": {
|
||||||
|
"text": "Pilot Reports",
|
||||||
|
"priority": 1
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"convective": [
|
"convective": [
|
||||||
"https://www.spc.noaa.gov/products/outlook/day1otlk.gif",
|
"https://weather.cod.edu/cdata/text/images/spc/co/day1/categorical/spccoday1.categorical.latest.png",
|
||||||
"https://www.spc.noaa.gov/products/outlook/day2otlk.gif",
|
"https://climate.cod.edu/data/text/images/spc/co/day2/categorical/spccoday2.categorical.latest.png",
|
||||||
"https://www.spc.noaa.gov/products/outlook/day3otlk.gif",
|
"https://climate.cod.edu/data/text/images/spc/co/day3/categorical/spccoday3.categorical.latest.png",
|
||||||
"https://www.spc.noaa.gov/products/exper/day4-8/day4prob.gif",
|
"https://climate.cod.edu/data/text/images/spc/co/day4/severe/spccoday4.severe.latest.png",
|
||||||
"https://www.spc.noaa.gov/products/exper/day4-8/day5prob.gif",
|
"https://climate.cod.edu/data/text/images/spc/co/day5/severe/spccoday5.severe.latest.png",
|
||||||
"https://www.spc.noaa.gov/products/exper/day4-8/day6prob.gif",
|
"https://climate.cod.edu/data/text/images/spc/co/day6/severe/spccoday6.severe.latest.png",
|
||||||
"https://www.spc.noaa.gov/products/exper/day4-8/day7prob.gif",
|
"https://climate.cod.edu/data/text/images/spc/co/day7/severe/spccoday7.severe.latest.png",
|
||||||
"https://www.spc.noaa.gov/products/exper/day4-8/day8prob.gif"
|
"https://climate.cod.edu/data/text/images/spc/co/day8/severe/spccoday8.severe.latest.png"
|
||||||
],
|
],
|
||||||
"fire": [
|
"fire": [
|
||||||
"https://www.spc.noaa.gov/products/exper/fire_wx/imgs/day1otlk_fire.gif",
|
"https://www.spc.noaa.gov/products/exper/fire_wx/imgs/day1otlk_fire.gif",
|
||||||
|
|
187
data/satellites.json
Normal file
187
data/satellites.json
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
{
|
||||||
|
"GOES-16": {
|
||||||
|
"products": {
|
||||||
|
"Full Disk": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/FD/GEOCOLOR/1808x1808.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/FD/AirMass/1808x1808.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/FD/13/1808x1808.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/FD/10/1808x1808.jpg"
|
||||||
|
},
|
||||||
|
"Floater 1": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/MESO/M1/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/MESO/M1/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/MESO/M1/10/1000x1000.jpg"
|
||||||
|
},
|
||||||
|
"Floater 2": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/MESO/M2/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/MESO/M2/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/MESO/M2/10/1000x1000.jpg"
|
||||||
|
},
|
||||||
|
"United States": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/CONUS/GEOCOLOR/2500x1500.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/CONUS/AirMass/2500x1500.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/CONUS/13/2500x1500.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/CONUS/10/2500x1500.jpg"
|
||||||
|
},
|
||||||
|
"Canada": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/can/GEOCOLOR/2250x1125.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/can/AirMass/2250x1125.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/can/13/2250x1125.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/can/10/2250x1125.jpg"
|
||||||
|
},
|
||||||
|
"Mexico": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/mex/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/mex/AirMass/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/mex/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/mex/10/1000x1000.jpg"
|
||||||
|
},
|
||||||
|
"US East Coast": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/eus/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/eus/AirMass/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/eus/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/eus/10/1000x1000.jpg"
|
||||||
|
},
|
||||||
|
"Gulf of Mexico": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/gm/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/gm/AirMass/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/gm/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/gm/10/1000x1000.jpg"
|
||||||
|
},
|
||||||
|
"Puerto Rico": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/pr/GEOCOLOR/1200x1200.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/pr/AirMass/1200x1200.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/pr/13/1200x1200.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/pr/10/1200x1200.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GOES-18": {
|
||||||
|
"products": {
|
||||||
|
"Full Disk": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/FD/GEOCOLOR/1808x1808.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/FD/AirMass/1808x1808.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/FD/13/1808x1808.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/FD/10/1808x1808.jpg"
|
||||||
|
},
|
||||||
|
"Floater 1": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/MESO/M1/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/MESO/M1/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/MESO/M1/10/1000x1000.jpg"
|
||||||
|
},
|
||||||
|
"Floater 2": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/MESO/M2/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/MESO/M2/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/MESO/M2/10/1000x1000.jpg"
|
||||||
|
},
|
||||||
|
"US West Coast": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/wus/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/wus/AirMass/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/wus/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/SECTOR/wus/10/1000x1000.jpg"
|
||||||
|
},
|
||||||
|
"Hawaii": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/SECTOR/hi/GEOCOLOR/1200x1200.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/SECTOR/hi/AirMass/1200x1200.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/SECTOR/hi/13/1200x1200.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/SECTOR/hi/10/1200x1200.jpg"
|
||||||
|
},
|
||||||
|
"Alaska": {
|
||||||
|
"Visible": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/SECTOR/ak/GEOCOLOR/1000x1000.jpg",
|
||||||
|
"Airmass": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/SECTOR/ak/AirMass/1000x1000.jpg",
|
||||||
|
"Infrared": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/SECTOR/ak/13/1000x1000.jpg",
|
||||||
|
"Water Vapor": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/SECTOR/ak/10/1000x1000.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Himawari": {
|
||||||
|
"products": {
|
||||||
|
"Full Disk": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/full_disk_ahi_true_color.jpg",
|
||||||
|
"Airmass": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/full_disk_ahi_rgb_airmass.jpg",
|
||||||
|
"Infrared": "https://www.ssec.wisc.edu/data/geo/images/himawari09/latest-himawari09_11_fd.gif",
|
||||||
|
"Water Vapor": "https://www.ssec.wisc.edu/data/geo/images/himawari09/latest-himawari09_10_fd.gif"
|
||||||
|
},
|
||||||
|
"Floater 1": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest/himawari-8/floater_02_geocolor.pngv",
|
||||||
|
"Airmass": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest/himawari-8/floater_02_rgb_airmass.png"
|
||||||
|
},
|
||||||
|
"American Samoa": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/american_samoa_ahi_natural_color.png",
|
||||||
|
"Airmass": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/american_samoa_ahi_rgb_airmass.png"
|
||||||
|
},
|
||||||
|
"Australia": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest/himawari-8/australia_true_color.jpg",
|
||||||
|
"Airmass": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/australia_ahi_rgb_airmass.png"
|
||||||
|
},
|
||||||
|
"New Zealand": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/new_zealand_ahi_natural_color.png",
|
||||||
|
"Airmass": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/new_zealand_ahi_rgb_airmass.png"
|
||||||
|
},
|
||||||
|
"Guam": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/guam_ahi_natural_color.png",
|
||||||
|
"Airmass": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/guam_ahi_rgb_airmass.png"
|
||||||
|
},
|
||||||
|
"Hawaii": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/hawaii_ahi_natural_color.png"
|
||||||
|
},
|
||||||
|
"Japan": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/japan_ahi_natural_color.png",
|
||||||
|
"Airmass": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/japan_ahi_rgb_airmass.png"
|
||||||
|
},
|
||||||
|
"Russia": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest/himawari-8/eastern_russia_true_color.jpg"
|
||||||
|
},
|
||||||
|
"China": {
|
||||||
|
"Visible": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/eastern_china_ahi_natural_color.png",
|
||||||
|
"Airmass": "https://rammb.cira.colostate.edu/ramsdis/online/images/latest_hi_res/himawari-8/eastern_china_ahi_rgb_airmass.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"EWS-G2": {
|
||||||
|
"products": {
|
||||||
|
"Full Disk": {
|
||||||
|
"Visible": "https://www.ssec.wisc.edu/data/geo/images/ews-g1/latest_ews-g1_01_fd.gif",
|
||||||
|
"Water Vapor": "https://www.ssec.wisc.edu/data/geo/images/ews-g1/latest_ews-g1_03_fd.gif"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"FY-2G": {
|
||||||
|
"products": {
|
||||||
|
"Full Disk": {
|
||||||
|
"Visible": "https://www.ssec.wisc.edu/data/geo/images/fy2g/latest_fy2g_01_fd.gif",
|
||||||
|
"Infrared": "https://www.ssec.wisc.edu/data/geo/images/fy2g/latest_fy2g_02_fd.gif",
|
||||||
|
"Water Vapor": "https://www.ssec.wisc.edu/data/geo/images/fy2g/latest_fy2g_04_fd.gif"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GK-2A": {
|
||||||
|
"products": {
|
||||||
|
"Full Disk": {
|
||||||
|
"Infrared": "https://kiwiweather.com/gk-2a/FD_sanchez.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Meteosat 9": {
|
||||||
|
"products": {
|
||||||
|
"Full Disk": {
|
||||||
|
"Visible": "https://www.ssec.wisc.edu/data/geo/images/met-iodc/latest_met-iodc_01_fd.jpg",
|
||||||
|
"Infrared": "https://www.ssec.wisc.edu/data/geo/images/met-iodc/latest_met-iodc_04_fd.jpg",
|
||||||
|
"Water Vapor": "https://www.ssec.wisc.edu/data/geo/images/met-iodc/latest_met-iodc_06_fd.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Meteosat 10": {
|
||||||
|
"products": {
|
||||||
|
"Full Disk": {
|
||||||
|
"Visible": "https://www.ssec.wisc.edu/data/geo/images/met-prime/latest_met-prime_01_fd.gif",
|
||||||
|
"Infrared": "https://www.ssec.wisc.edu/data/geo/images/met-prime/latest_met-prime_04_fd.gif",
|
||||||
|
"Water Vapor": "https://www.ssec.wisc.edu/data/geo/images/met-prime/latest_met-prime_06_fd.gif"
|
||||||
|
},
|
||||||
|
"Europe": {
|
||||||
|
"Visible": "https://www.ssec.wisc.edu/data/geo/images/met-prime/latest_met-prime_01_euro.gif",
|
||||||
|
"Infrared": "https://www.ssec.wisc.edu/data/geo/images/met-prime/latest_met-prime_04_euro.gif",
|
||||||
|
"Water Vapor": "https://www.ssec.wisc.edu/data/geo/images/met-prime/latest_met-prime_06_euro.gif"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"GOES-16": [
|
|
||||||
{
|
|
||||||
"name": "GeoColor",
|
|
||||||
"url": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/CONUS/GEOCOLOR/latest.jpg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Infrared",
|
|
||||||
"url": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/CONUS/13/latest.jpg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FullDisk",
|
|
||||||
"url": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/FD/GEOCOLOR/678x678.jpg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"GOES-18": [
|
|
||||||
{
|
|
||||||
"name": "GeoColor",
|
|
||||||
"url": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/CONUS/GEOCOLOR/latest.jpg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Infrared",
|
|
||||||
"url": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/CONUS/13/latest.jpg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FullDisk",
|
|
||||||
"url": "https://cdn.star.nesdis.noaa.gov/GOES18/ABI/FD/GEOCOLOR/678x678.jpg"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -14,7 +14,6 @@
|
||||||
"@xmpp/client": "^0.13.1",
|
"@xmpp/client": "^0.13.1",
|
||||||
"@xmpp/debug": "^0.13.0",
|
"@xmpp/debug": "^0.13.0",
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"discord-hybrid-sharding": "^2.2.0",
|
|
||||||
"discord.js": "14.14.1",
|
"discord.js": "14.14.1",
|
||||||
"geolib": "^3.3.4",
|
"geolib": "^3.3.4",
|
||||||
"html-entities": "^2.5.2",
|
"html-entities": "^2.5.2",
|
||||||
|
|
572
shard.js
572
shard.js
|
@ -1,572 +0,0 @@
|
||||||
const config = require("./config.json");
|
|
||||||
const {
|
|
||||||
REST,
|
|
||||||
Routes
|
|
||||||
} = require('discord.js');
|
|
||||||
const rest = new REST({
|
|
||||||
version: '10'
|
|
||||||
}).setToken(config.discord.token);
|
|
||||||
const sqlite3 = require("sqlite3").verbose();
|
|
||||||
|
|
||||||
// Setup SQlite DB
|
|
||||||
const db = new sqlite3.Database("channels.db", (err) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(`${colors.red("[ERROR]")} Error connecting to database: ${err.message}`);
|
|
||||||
}
|
|
||||||
console.log(`${colors.cyan("[INFO]")} Connected to the database`);
|
|
||||||
// Create tables if they dont exist
|
|
||||||
db.run(`CREATE TABLE IF NOT EXISTS channels (channelid TEXT, iemchannel TEXT, custommessage TEXT, minPriority INTEGER, "filter" TEXT, filterevt TEXT);`);
|
|
||||||
db.run(`CREATE TABLE IF NOT EXISTS userAlerts (userid TEXT, iemchannel TEXT, filter TEXT, filterEvt TEXT, minPriority INT, custommessage TEXT);`);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Requires
|
|
||||||
const fs = require("fs");
|
|
||||||
const funcs = require("./funcs.js");
|
|
||||||
const wfos = require("./data/wfos.json");
|
|
||||||
const blacklist = require("./data/blacklist.json");
|
|
||||||
const events = require("./data/events.json");
|
|
||||||
const outlookURLs = require("./data/outlook.json");
|
|
||||||
const sattelites = require("./data/sattelites.json");
|
|
||||||
const nwrstreams = require("./data/nwrstreams.json")
|
|
||||||
const Jimp = require("jimp");
|
|
||||||
const { client, xml } = require("@xmpp/client");
|
|
||||||
const fetch = require("node-fetch");
|
|
||||||
const html = require("html-entities")
|
|
||||||
const Discord = require("discord.js");
|
|
||||||
const dVC = require("@discordjs/voice");
|
|
||||||
const colors = require("colors");
|
|
||||||
|
|
||||||
|
|
||||||
const parseProductID = function (product_id) {
|
|
||||||
const [timestamp, station, wmo, pil] = product_id.split("-");
|
|
||||||
return {
|
|
||||||
timestamp: convertDate(timestamp),
|
|
||||||
originalTimestamp: timestamp,
|
|
||||||
station,
|
|
||||||
wmo,
|
|
||||||
pil
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert date format 202405080131 (YYYYMMddHHmm) to iso format, hours and mins is UTC
|
|
||||||
const convertDate = function (date) {
|
|
||||||
const year = date.substring(0, 4);
|
|
||||||
const month = date.substring(4, 6);
|
|
||||||
const day = date.substring(6, 8);
|
|
||||||
const hours = date.substring(8, 10);
|
|
||||||
const mins = date.substring(10, 12);
|
|
||||||
// Because they don't have seconds, assume current seconds
|
|
||||||
const secs = new Date().getSeconds();
|
|
||||||
return new Date(Date.UTC(year, month - 1, day, hours, mins, secs));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get first url in a string, return object {string, url} remove the url from the string
|
|
||||||
const getFirstURL = function (string) {
|
|
||||||
url = string.match(/(https?:\/\/[^\s]+)/g);
|
|
||||||
if (!url) return { string, url: null };
|
|
||||||
const newString = string.replace(url[0], "");
|
|
||||||
return { string: newString, url: url[0] };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get WFO data from room name
|
|
||||||
|
|
||||||
function getWFOByRoom(room) {
|
|
||||||
room = room.toLowerCase();
|
|
||||||
for (const key in wfos) {
|
|
||||||
if (wfos.hasOwnProperty(key) && wfos[key].room === room) {
|
|
||||||
return wfos[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
location: room,
|
|
||||||
room: room
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Voice funcs
|
|
||||||
function JoinChannel(channel, track, volume, message) {
|
|
||||||
connection = dVC.joinVoiceChannel({
|
|
||||||
channelId: channel.id,
|
|
||||||
guildId: channel.guild.id,
|
|
||||||
adapterCreator: channel.guild.voiceAdapterCreator,
|
|
||||||
selfDeaf: true
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
resource = dVC.createAudioResource(track, { inlineVolume: true, silencePaddingFrames: 5 });
|
|
||||||
player = dVC.createAudioPlayer();
|
|
||||||
connection.player = player; // So we can access it later to pause/play/stop etc
|
|
||||||
resource.volume.setVolume(volume);
|
|
||||||
connection.subscribe(player)
|
|
||||||
player.play(resource);
|
|
||||||
connection.on(dVC.VoiceConnectionStatus.Ready, () => { player.play(resource); })
|
|
||||||
connection.on(dVC.VoiceConnectionStatus.Disconnected, async (oldState, newState) => {
|
|
||||||
try {
|
|
||||||
await Promise.race([
|
|
||||||
dVC.entersState(connection, VoiceConnectionStatus.Signalling, 5_000),
|
|
||||||
dVC.entersState(connection, VoiceConnectionStatus.Connecting, 5_000),
|
|
||||||
]);
|
|
||||||
} catch (error) {
|
|
||||||
message.channel.send(`Failed to reconnect to the voice channel. Stopping for now.`);
|
|
||||||
connection.destroy();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
player.on('error', error => {
|
|
||||||
console.error(`Error: ${error.message} with resource ${error.resource.metadata.title}`);
|
|
||||||
message.channel.send(`Error while streaming. Stopping for now.`);
|
|
||||||
player.stop();
|
|
||||||
});
|
|
||||||
player.on(dVC.AudioPlayerStatus.Playing, () => {
|
|
||||||
message.channel.send(`Playing stream in <#${channel.id}>`);
|
|
||||||
connection.paused = false;
|
|
||||||
});
|
|
||||||
player.on('idle', () => {
|
|
||||||
message.channel.send(`Stream idle.`);
|
|
||||||
})
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LeaveVoiceChannel(channel) {
|
|
||||||
// Get resource, player, etc, and destroy them
|
|
||||||
const connection = dVC.getVoiceConnection(channel.guild.id);
|
|
||||||
if (connection) {
|
|
||||||
connection.destroy();
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleVoicePause(channel) {
|
|
||||||
const connection = dVC.getVoiceConnection(channel.guild.id);
|
|
||||||
if (connection) {
|
|
||||||
if (connection.paused) {
|
|
||||||
connection.player.unpause();
|
|
||||||
connection.paused = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
connection.player.pause();
|
|
||||||
connection.paused = true;
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function setVolume(channel, volume) {
|
|
||||||
const connection = dVC.getVoiceConnection(channel.guild.id);
|
|
||||||
if (connection) {
|
|
||||||
connection.player.state.resource.volume.setVolume(volume);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func to Generate random string, ({upper, lower, number, special}, length)
|
|
||||||
|
|
||||||
const generateRandomString = function (options, length) {
|
|
||||||
let result = '';
|
|
||||||
const characters = {
|
|
||||||
upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
|
||||||
lower: 'abcdefghijklmnopqrstuvwxyz',
|
|
||||||
number: '0123456789',
|
|
||||||
special: '!@#$%^&*()_+'
|
|
||||||
};
|
|
||||||
let chars = '';
|
|
||||||
for (const key in options) {
|
|
||||||
if (options[key]) {
|
|
||||||
chars += characters[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Func to generate UUID
|
|
||||||
const generateUUID = function () {
|
|
||||||
return generateRandomString({ lower: true, upper: true, number: true }, 8) + "-" + generateRandomString({ lower: true, upper: true, number: true }, 4) + "-" + generateRandomString({ lower: true, upper: true, number: true }, 4) + "-" + generateRandomString({ lower: true, upper: true, number: true }, 4) + "-" + generateRandomString({ lower: true, upper: true, number: true }, 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variable setup
|
|
||||||
var iem = []
|
|
||||||
var startup = true;
|
|
||||||
var messages = 0;
|
|
||||||
var errCount = 0;
|
|
||||||
const curUUID = generateUUID();
|
|
||||||
|
|
||||||
|
|
||||||
const xmpp = client({
|
|
||||||
service: "xmpp://conference.weather.im",
|
|
||||||
domain: "weather.im",
|
|
||||||
resource: `discord-weather-bot-${generateRandomString({ upper: true, lower: true, number: true }, 5)}`, // Weird fix to "Username already in use"
|
|
||||||
});
|
|
||||||
|
|
||||||
//debug(xmpp, true);
|
|
||||||
|
|
||||||
xmpp.on("error", (err) => {
|
|
||||||
console.log(`${colors.red("[ERROR]")} XMPP Error: ${err}. Trying to reconnect...`);
|
|
||||||
setTimeout(() => {
|
|
||||||
xmpp.stop().then(() => {
|
|
||||||
start();
|
|
||||||
});
|
|
||||||
}, 5000);
|
|
||||||
});
|
|
||||||
|
|
||||||
xmpp.on("offline", () => {
|
|
||||||
console.log(`${colors.yellow("[WARN]")} XMPP offline, trying to reconnect...`);
|
|
||||||
xmpp.disconnect().then(() => {
|
|
||||||
xmpp.stop().then(() => {
|
|
||||||
start();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
xmpp.on("stanza", (stanza) => {
|
|
||||||
// Debug stuff
|
|
||||||
if (config.debug >= 2) console.log(`${colors.magenta("[DEBUG]")} Stanza: ${stanza.toString()}`);
|
|
||||||
|
|
||||||
|
|
||||||
// Handle Room List
|
|
||||||
if (stanza.is("iq") && stanza.attrs.type === "result" && stanza.getChild("query")) {
|
|
||||||
query = stanza.getChild("query");
|
|
||||||
if (query.attrs.xmlns === "http://jabber.org/protocol/disco#items") {
|
|
||||||
query.getChildren("item").forEach((item) => {
|
|
||||||
// Check if the JID is on the blacklist, if so, ignore it
|
|
||||||
if (blacklist.includes(item.attrs.jid)) return;
|
|
||||||
// get proper name from wfos
|
|
||||||
const wfo = getWFOByRoom(item.attrs.jid.split("@")[0]);
|
|
||||||
item.attrs.properName = wfo.location;
|
|
||||||
iem.push(item.attrs);
|
|
||||||
console.log(`${colors.cyan("[INFO]")} Found room: ${item.attrs.jid}`);
|
|
||||||
// Join the room
|
|
||||||
//xmpp.send(xml("presence", { to: `${channel.jid}/${channel.name}/${curUUID}` }, xml("item", { role: "visitor" })));
|
|
||||||
xmpp.send(xml("presence", { to: `${item.attrs.jid}/${curUUID}` }, xml("item", { role: "visitor" })));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get new messages and log them, ignore old messages
|
|
||||||
if (stanza.is("message") && stanza.attrs.type === "groupchat") {
|
|
||||||
// Stops spam from getting old messages
|
|
||||||
if (startup) return;
|
|
||||||
// Get channel name
|
|
||||||
fromChannel = stanza.attrs.from.split("@")[0];
|
|
||||||
// Ignores
|
|
||||||
if (!stanza.getChild("x")) return; // No PID, ignore it
|
|
||||||
if (!stanza.getChild("x").attrs.product_id) return;
|
|
||||||
|
|
||||||
const product_id = parseProductID(stanza.getChild("x").attrs.product_id);
|
|
||||||
const product_id_raw = stanza.getChild("x").attrs.product_id;
|
|
||||||
// Get body of message
|
|
||||||
const body = html.decode(stanza.getChildText("body"));
|
|
||||||
const bodyData = getFirstURL(body);
|
|
||||||
// get product id from "x" tag
|
|
||||||
var evt = events[product_id.pil.substring(0, 3)];
|
|
||||||
|
|
||||||
if (!evt) {
|
|
||||||
evt = { name: "Unknown", priority: 3 }
|
|
||||||
console.log(`${colors.red("[ERROR]")} Unknown event type: ${product_id.pil.substring(0, 3)}. Fix me`);
|
|
||||||
console.log(`${colors.magenta("[DEBUG]")} ${bodyData.string}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
evt.code = product_id.pil.substring(0, 3);
|
|
||||||
// Check timestamp, if not within 3 minutes, ignore it
|
|
||||||
const now = new Date();
|
|
||||||
const diff = (now - product_id.timestamp) / 1000 / 60;
|
|
||||||
if (diff > 3) return;
|
|
||||||
if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} New message from ${fromChannel}`);
|
|
||||||
messages++;
|
|
||||||
|
|
||||||
|
|
||||||
// Handle NTFY
|
|
||||||
if (config.ntfy.enabled) {
|
|
||||||
if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} Sending NTFY for ${config.ntfy.prefix}${fromChannel}`)
|
|
||||||
ntfyBody = {
|
|
||||||
"topic": `${config.ntfy.prefix}${fromChannel}`,
|
|
||||||
"message": bodyData.string,
|
|
||||||
"tags": [`Timestamp: ${product_id.timestamp}`, `Station: ${product_id.station}`, `WMO: ${product_id.wmo}`, `PIL: ${product_id.pil}`, `Channel: ${fromChannel}`],
|
|
||||||
"priority": evt.priority,
|
|
||||||
"actions": [{ "action": "view", "label": "Product", "url": bodyData.url }, { "action": "view", "label": "Product Text", "url": `https://mesonet.agron.iastate.edu/api/1/nwstext/${product_id_raw}` }]
|
|
||||||
}
|
|
||||||
if (stanza.getChild("x").attrs.twitter_media) {
|
|
||||||
ntfyBody.attach = stanza.getChild("x").attrs.twitter_media;
|
|
||||||
}
|
|
||||||
fetch(config.ntfy.server, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(ntfyBody),
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${config.ntfy.token}`
|
|
||||||
}
|
|
||||||
}).then((res) => {
|
|
||||||
if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} NTFY sent for ${config.ntfy.prefix}${fromChannel} with status ${res.status} ${res.statusText}`);
|
|
||||||
if (res.status !== 200) console.log(`${colors.red("[ERROR]")} NTFY failed for ${config.ntfy.prefix}${fromChannel} with status ${res.status} ${res.statusText}`);
|
|
||||||
|
|
||||||
|
|
||||||
}).catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Send discord msg
|
|
||||||
let embed = {
|
|
||||||
description: `<t:${product_id.timestamp / 1000}:T> <t:${product_id.timestamp / 1000}:R> ${bodyData.string}`,
|
|
||||||
color: parseInt(config.priorityColors[evt.priority].replace("#", ""), 16) || 0x000000,
|
|
||||||
timestamp: product_id.timestamp,
|
|
||||||
footer: {
|
|
||||||
text: `Station: ${product_id.station} PID: ${product_id_raw} Channel: ${fromChannel}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stanza.getChild("x").attrs.twitter_media) {
|
|
||||||
embed.image = {
|
|
||||||
url: stanza.getChild("x").attrs.twitter_media
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let discordMsg = {
|
|
||||||
embeds: [embed],
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
type: 1,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
type: 2,
|
|
||||||
label: "Product",
|
|
||||||
style: 5,
|
|
||||||
url: bodyData.url
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 2,
|
|
||||||
style: 1,
|
|
||||||
custom_id: product_id_raw,
|
|
||||||
label: "Product Text",
|
|
||||||
emoji: {
|
|
||||||
name: "📄"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
// Discord Channel Handling
|
|
||||||
db.all(`SELECT * FROM channels WHERE iemchannel = ?`, [fromChannel], (err, rows) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(`${colors.red("[ERROR]")} ${err.message}`);
|
|
||||||
}
|
|
||||||
if (!rows) return; // No channels to alert
|
|
||||||
rows.forEach((row) => {
|
|
||||||
// Get Filters as arrays
|
|
||||||
if (!row.filterEvt) row.filterEvt = "";
|
|
||||||
if (!row.filter) row.filter = "";
|
|
||||||
let filterEvt = row.filterEvt.toLowerCase().split(",");
|
|
||||||
let filters = row.filter.toLowerCase().split(",");
|
|
||||||
if (evt.priority < row.minPriority) return;
|
|
||||||
// If the event type is not in th filter, ignore it. Make sure filterEvt isnt null
|
|
||||||
if (!filterEvt[0]) filterEvt = [];
|
|
||||||
if (!filterEvt.includes(evt.code.toLowerCase()) && !filterEvt.length == 0) return;
|
|
||||||
|
|
||||||
// fetch the product text
|
|
||||||
trySend = () => {
|
|
||||||
fetch(`https://mesonet.agron.iastate.edu/api/1/nwstext/${product_id_raw}`).then((res) => {
|
|
||||||
// If neither the body nor the product text contains the filter, ignore it
|
|
||||||
res.text().then((text) => {
|
|
||||||
if (!filters.some((filter) => body.toLowerCase().includes(filter)) && !filters.some((filter) => text.toLowerCase().includes(filter))) return;
|
|
||||||
thisMsg = JSON.parse(JSON.stringify(discordMsg));
|
|
||||||
thisMsg.content = row.custommessage || null;
|
|
||||||
// set channelid var in shard
|
|
||||||
channelid = row.channelid;
|
|
||||||
manager.broadcast({
|
|
||||||
type: "sendMsgChannel",
|
|
||||||
channel: channelid,
|
|
||||||
msg: thisMsg
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}).catch((err) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log(`${colors.red("[ERROR]")} Failed to fetch product text, retrying... ${err}`)
|
|
||||||
trySend();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
trySend();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// User DM alert handling
|
|
||||||
db.all(`SELECT * FROM userAlerts WHERE iemchannel = ?`, [fromChannel], (err, rows) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
}
|
|
||||||
if (!rows) return; // No users to alert
|
|
||||||
rows.forEach((row) => {
|
|
||||||
// Get Filters as arrays
|
|
||||||
if (!row.filterEvt) row.filterEvt = "";
|
|
||||||
if (!row.filter) row.filter = "";
|
|
||||||
let filterEvt = row.filterEvt.toLowerCase().split(",");
|
|
||||||
let filters = row.filter.toLowerCase().split(",");
|
|
||||||
|
|
||||||
// If priority is less than the min priority, ignore it
|
|
||||||
if (evt.priority < row.minPriority) return;
|
|
||||||
// If the event type is not in th filter, ignore it. Make sure filterEvt isnt null
|
|
||||||
if (!filterEvt[0]) filterEvt = [];
|
|
||||||
if (!filterEvt.includes(evt.code.toLowerCase()) && !filterEvt.length == 0) return;
|
|
||||||
|
|
||||||
// fetch the product text
|
|
||||||
trySend = () => {
|
|
||||||
fetch(`https://mesonet.agron.iastate.edu/api/1/nwstext/${product_id_raw}`).then((res) => {
|
|
||||||
// If neither the body nor the product text contains the filter, ignore it
|
|
||||||
res.text().then((text) => {
|
|
||||||
if (!filters.some((filter) => body.toLowerCase().includes(filter)) && !filters.some((filter) => text.toLowerCase().includes(filter))) return;
|
|
||||||
thisMsg = JSON.parse(JSON.stringify(discordMsg));
|
|
||||||
thisMsg.content = row.custommessage || null;
|
|
||||||
// set channelid var in shard
|
|
||||||
userid = row.userid;
|
|
||||||
manager.broadcast({
|
|
||||||
type: "sendMsgUser",
|
|
||||||
user: userid,
|
|
||||||
msg: thisMsg
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}).catch((err) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log(`${colors.red("[ERROR]")} Failed to fetch product text, retrying... ${err}`)
|
|
||||||
trySend();
|
|
||||||
})
|
|
||||||
});;
|
|
||||||
}
|
|
||||||
trySend();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
xmpp.on("online", async (address) => {
|
|
||||||
if (config["uptime-kuma"].enabled) {
|
|
||||||
fetch(config["uptime-kuma"].url).then(() => {
|
|
||||||
console.log(`${colors.cyan("[INFO]")} Sent heartbeat to Uptime Kuma`)
|
|
||||||
})
|
|
||||||
setInterval(() => {
|
|
||||||
// Send POST request to config["uptime-kuma"].url
|
|
||||||
fetch(config["uptime-kuma"].url).then(() => {
|
|
||||||
console.log(`${colors.cyan("[INFO]")} Sent heartbeat to Uptime Kuma`)
|
|
||||||
})
|
|
||||||
}, config["uptime-kuma"].interval * 1000) // Every X seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
errCount = 0;
|
|
||||||
// Start listening on all channels, (dont ban me funny man)
|
|
||||||
// for (const channel in iem) {
|
|
||||||
// console.log(`Joining ${channel.name}`)
|
|
||||||
// await xmpp.send(xml("presence", { to: `${channel.jud}/${channel.name}` }));
|
|
||||||
// }
|
|
||||||
/* sub format
|
|
||||||
<presence to="botstalk@conference.weather.im/add9b8f1-038d-47ed-b708-6ed60075a82f" xmlns="jabber:client">
|
|
||||||
<x xmlns="http://jabber.org/protocol/muc#user">
|
|
||||||
<item>
|
|
||||||
<role>visitor</role>
|
|
||||||
</item>
|
|
||||||
</x>
|
|
||||||
</presence>
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Request room list
|
|
||||||
// Automatically find room list
|
|
||||||
xmpp.send(xml("iq", { type: "get", to: "conference.weather.im", id: "rooms" }, xml("query", { xmlns: "http://jabber.org/protocol/disco#items" })));
|
|
||||||
// Join all channels (Old method)
|
|
||||||
// iem.forEach((channel => {
|
|
||||||
// console.log(`${colors.cyan("[INFO]")} Joining ${channel.jid}/${channel.name}/${curUUID}`)
|
|
||||||
// //xmpp.send(xml("presence", { to: `${channel.jid}/${channel.jid.split("@")[0]}` }));
|
|
||||||
// xmpp.send(xml("presence", { to: `${channel.jid}/${channel.name}/${curUUID}` }, xml("item", { role: "visitor" })));
|
|
||||||
// }))
|
|
||||||
|
|
||||||
console.log(`${colors.cyan("[INFO]")} Connected to XMPP server as ${address.toString()}`);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
startup = false;
|
|
||||||
console.log(`${colors.cyan("[INFO]")} Startup complete, listening for messages...`);
|
|
||||||
}, 1000)
|
|
||||||
});
|
|
||||||
|
|
||||||
xmpp.on("close", () => {
|
|
||||||
console.log(`${colors.yellow("[WARN]")} XMPP connection closed, trying to reconnect...`);
|
|
||||||
xmpp.disconnect().then(() => {
|
|
||||||
xmpp.stop().then(() => {
|
|
||||||
start();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const start = () => {
|
|
||||||
startup = true;
|
|
||||||
xmpp.start().catch((err) => {
|
|
||||||
errCount++;
|
|
||||||
if (errCount >= 5) {
|
|
||||||
console.log(`${colors.red("[ERROR]")} XMPP failed to start after 5 attempts, exiting...`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
console.log(`${colors.red("[ERROR]")} XMPP failed to start: ${err}.`);
|
|
||||||
xmpp.disconnect().then(() => {
|
|
||||||
xmpp.stop().then(() => {
|
|
||||||
start();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// END XMPP
|
|
||||||
|
|
||||||
process.on("unhandledRejection", (error, promise) => {
|
|
||||||
console.log(`${colors.red("[ERROR]")} Unhandled Rejection @ ${JSON.stringify(promise)}: ${error.stack}`);
|
|
||||||
// create errors folder if it doesnt exist
|
|
||||||
if (!fs.existsSync("./error")) {
|
|
||||||
fs.mkdirSync("./error");
|
|
||||||
}
|
|
||||||
// write ./error/rejection_timestamp.txt
|
|
||||||
fs.writeFileSync(`./error/rejection_${Date.now()}.txt`, `ERROR:\n${error.stack}\n\nPROMISE:\n${JSON.stringify(promise)}`);
|
|
||||||
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on("uncaughtException", (error) => {
|
|
||||||
|
|
||||||
console.log(`${colors.red("[ERROR]")} Uncaught Exception: ${error.message}\n${error.stack}`);
|
|
||||||
if (!fs.existsSync("./error")) {
|
|
||||||
fs.mkdirSync("./error");
|
|
||||||
}
|
|
||||||
// write ./error/exception_timestamp.txt
|
|
||||||
fs.writeFileSync(`./error/exception_${Date.now()}.txt`, error.stack);
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
const { ShardingManager } = require('discord.js');
|
|
||||||
const { ClusterManager } = require('discord-hybrid-sharding');
|
|
||||||
const manager = new ClusterManager(`${__dirname}/index.js`, {
|
|
||||||
totalShards: 2, // or numeric shard count
|
|
||||||
/// Check below for more options
|
|
||||||
shardsPerClusters: 1, // 2 shards per process
|
|
||||||
totalClusters: 1,
|
|
||||||
mode: 'process', // you can also choose "worker"
|
|
||||||
token: config.discord.token,
|
|
||||||
});
|
|
||||||
|
|
||||||
manager.on('clusterCreate', (cluster) => {
|
|
||||||
setInterval(() => {cluster.send({ type: "yourId", data: cluster.id})}, 1000);
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
manager.spawn({timeout: -1}).then(() => {
|
|
||||||
start();
|
|
||||||
console.log(`${colors.cyan("[INFO]")} Spawned all shards`);
|
|
||||||
setInterval(() => {
|
|
||||||
manager.broadcast({ type: "heartbeat" });
|
|
||||||
manager.broadcast({ type: "updateRooms", rooms: iem})
|
|
||||||
}, 1000)
|
|
||||||
});
|
|
Loading…
Reference in a new issue