main #21

Open
ChrisChrome wants to merge 44 commits from main into shard-test
10 changed files with 592 additions and 160 deletions

1
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1 @@
{}

7
PRIVACY.md Normal file
View file

@ -0,0 +1,7 @@
# Privacy Policy
Just to make this simple, heres a list of whats stored and how it's used
- Discord channel/user IDs of subscribed channels/DMs - Should be obvious, in case it's not, we need these to know where to send alerts.
- IEM rooms and filters - Should also be obvious, but again, we need to know which rooms you subscribed to
Other than that, occasional debug logging may be enabled to fix bugs, and any logs gathered from debugging will be erased immidiately.

5
TERMS.md Normal file
View file

@ -0,0 +1,5 @@
# Terms of Service
## This is mostly here to comply with Discord verified bot stuff
Other than complying with the GPL-3.0 License when contributing/using the code of this project, this bot is NOT to be used as an emergency alerting system, and should NOT be trusted with life/property under any circumstances.
Chris Chrome and any other contributors are not to be held liable should this not work in an emergency, as you shouldn't be using it for emergency alerts.

View file

@ -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"
] ]

View file

@ -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
} }
} }

View file

@ -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
View 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"
}
}
}
}

View file

@ -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"
}
]
}

475
index.js
View file

@ -6,8 +6,8 @@ const wfos = require("./data/wfos.json");
const blacklist = require("./data/blacklist.json"); const blacklist = require("./data/blacklist.json");
const events = require("./data/events.json"); const events = require("./data/events.json");
const outlookURLs = require("./data/outlook.json"); const outlookURLs = require("./data/outlook.json");
const sattelites = require("./data/sattelites.json"); const satellites = require("./data/satellites.json");
const nwrstreams = require("./data/nwrstreams.json") const nwrstreams = {callsigns:{}};
const Jimp = require("jimp"); const Jimp = require("jimp");
const { client, xml } = require("@xmpp/client"); const { client, xml } = require("@xmpp/client");
const fetch = require("node-fetch"); const fetch = require("node-fetch");
@ -16,8 +16,12 @@ const Discord = require("discord.js");
const dVC = require("@discordjs/voice"); const dVC = require("@discordjs/voice");
const colors = require("colors"); const colors = require("colors");
const sqlite3 = require("sqlite3").verbose(); const sqlite3 = require("sqlite3").verbose();
satMessages = {};
// Setup Discord // Setup Discord
const discord = new Discord.Client({ const discord = new Discord.Client({
intents: [ intents: [
"Guilds", "Guilds",
"GuildVoiceStates", "GuildVoiceStates",
@ -32,7 +36,6 @@ const rest = new REST({
version: '10' version: '10'
}).setToken(config.discord.token); }).setToken(config.discord.token);
// Setup SQlite DB // Setup SQlite DB
const db = new sqlite3.Database("channels.db", (err) => { const db = new sqlite3.Database("channels.db", (err) => {
if (err) { if (err) {
@ -40,8 +43,9 @@ const db = new sqlite3.Database("channels.db", (err) => {
} }
console.log(`${colors.cyan("[INFO]")} Connected to the database`); console.log(`${colors.cyan("[INFO]")} Connected to the database`);
// Create tables if they dont exist // 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 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);`); db.run(`CREATE TABLE IF NOT EXISTS userAlerts (userid TEXT, iemchannel TEXT, filter TEXT, filterEvt TEXT, minPriority INT, custommessage TEXT);`);
db.run(`ALTER TABLE channels RENAME COLUMN filterevt TO filterEvt;`)
}); });
@ -85,7 +89,17 @@ const getUniqueChannels = function () {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
} }
resolve(rows.length); // Go through channels. and get number of unique guilds
const guilds = [];
rows.forEach((row) => {
const channel = discord.channels.cache.get(row.channelid);
if (!channel) return;
if (!guilds.includes(channel.guild.id)) {
guilds.push(channel.guild.id);
}
});
resolve({ channels: rows.length, guilds: guilds.length });
}); });
}); });
} }
@ -250,6 +264,24 @@ var errCount = 0;
const curUUID = generateUUID(); const curUUID = generateUUID();
// nwrstreams setup
// get icecast json data
const fetchNWRstreams = () => {
fetch("https://icestats.weatherradio.org/").then((res) => {
res.json().then((json) => {
json.icestats.source.forEach((source) => {
nwrstreams.callsigns[source.server_name] = source.listenurl;
});
});
console.log(`${colors.cyan("[INFO]")} Fetched NWR streams`);
}).catch((err) => {
console.error(err);
});
}
fetchNWRstreams();
setInterval(fetchNWRstreams, 5 * 60 * 1000); // Every 5 minutes
const xmpp = client({ const xmpp = client({
service: "xmpp://conference.weather.im", service: "xmpp://conference.weather.im",
domain: "weather.im", domain: "weather.im",
@ -276,6 +308,8 @@ xmpp.on("offline", () => {
}) })
}); });
var restartTimer = null;
xmpp.on("stanza", (stanza) => { xmpp.on("stanza", (stanza) => {
// Debug stuff // Debug stuff
if (config.debug >= 2) console.log(`${colors.magenta("[DEBUG]")} Stanza: ${stanza.toString()}`); if (config.debug >= 2) console.log(`${colors.magenta("[DEBUG]")} Stanza: ${stanza.toString()}`);
@ -301,6 +335,11 @@ xmpp.on("stanza", (stanza) => {
} }
// Get new messages and log them, ignore old messages // Get new messages and log them, ignore old messages
if (stanza.is("message") && stanza.attrs.type === "groupchat") { if (stanza.is("message") && stanza.attrs.type === "groupchat") {
clearTimeout(restartTimer)
restartTimer = setTimeout(() => {
console.log(`${colors.red("[FATAL]")} No messages from weather.im in 10 minutes, restarting!!!!!!!!!!!`)
process.exit(1)
}, 600000)
// Stops spam from getting old messages // Stops spam from getting old messages
if (startup) return; if (startup) return;
// Get channel name // Get channel name
@ -321,6 +360,16 @@ xmpp.on("stanza", (stanza) => {
evt = { name: "Unknown", priority: 3 } evt = { name: "Unknown", priority: 3 }
console.log(`${colors.red("[ERROR]")} Unknown event type: ${product_id.pil.substring(0, 3)}. Fix me`); console.log(`${colors.red("[ERROR]")} Unknown event type: ${product_id.pil.substring(0, 3)}. Fix me`);
console.log(`${colors.magenta("[DEBUG]")} ${bodyData.string}`) console.log(`${colors.magenta("[DEBUG]")} ${bodyData.string}`)
const logChannel = discord.guilds.cache.get(config.discord.mainGuild).channels.cache.get(config.discord.logChannel);
logChannel.send({
embeds: [
{
title: "Unknown Event Type",
description: `Unknown event type: ${product_id.pil.substring(0, 3)}. Please check the logs for more details.`,
color: 0xff0000
}
]
});
} }
evt.code = product_id.pil.substring(0, 3); evt.code = product_id.pil.substring(0, 3);
@ -328,13 +377,14 @@ xmpp.on("stanza", (stanza) => {
const now = new Date(); const now = new Date();
const diff = (now - product_id.timestamp) / 1000 / 60; const diff = (now - product_id.timestamp) / 1000 / 60;
if (diff > 3) return; if (diff > 3) return;
if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} New message from ${fromChannel}`); // if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} New message from ${fromChannel}`);
console.log(`${colors.cyan("[INFO]")} ${getWFOByRoom(fromChannel).location} - ${evt.text} - ${product_id.timestamp}`);
messages++; messages++;
// Handle NTFY // Handle NTFY
if (config.ntfy.enabled) { if (config.ntfy.enabled) {
if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} Sending NTFY for ${config.ntfy.prefix}${fromChannel}`) //if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} Sending NTFY for ${config.ntfy.prefix}${fromChannel}`)
ntfyBody = { ntfyBody = {
"topic": `${config.ntfy.prefix}${fromChannel}`, "topic": `${config.ntfy.prefix}${fromChannel}`,
"message": bodyData.string, "message": bodyData.string,
@ -352,7 +402,7 @@ xmpp.on("stanza", (stanza) => {
'Authorization': `Bearer ${config.ntfy.token}` 'Authorization': `Bearer ${config.ntfy.token}`
} }
}).then((res) => { }).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 (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}`); if (res.status !== 200) console.log(`${colors.red("[ERROR]")} NTFY failed for ${config.ntfy.prefix}${fromChannel} with status ${res.status} ${res.statusText}`);
@ -392,7 +442,7 @@ xmpp.on("stanza", (stanza) => {
{ {
type: 2, type: 2,
style: 1, style: 1,
custom_id: product_id_raw, custom_id: `product|${product_id_raw}`,
label: "Product Text", label: "Product Text",
emoji: { emoji: {
name: "📄" name: "📄"
@ -408,7 +458,7 @@ xmpp.on("stanza", (stanza) => {
console.log(`${colors.red("[ERROR]")} ${err.message}`); console.log(`${colors.red("[ERROR]")} ${err.message}`);
} }
if (!rows) return; // No channels to alert if (!rows) return; // No channels to alert
rows.forEach((row) => { rows.forEach(async (row) => {
// Get Filters as arrays // Get Filters as arrays
if (!row.filterEvt) row.filterEvt = ""; if (!row.filterEvt) row.filterEvt = "";
if (!row.filter) row.filter = ""; if (!row.filter) row.filter = "";
@ -434,6 +484,54 @@ xmpp.on("stanza", (stanza) => {
console.error(err); console.error(err);
}).then((msg) => { }).then((msg) => {
if (msg.channel.type === Discord.ChannelType.GuildAnnouncement) msg.crosspost(); if (msg.channel.type === Discord.ChannelType.GuildAnnouncement) msg.crosspost();
}).catch((err) => {
console.log(`${colors.yellow("[WARN]")} Failed to send message to ${channel.guild.name}/${channel.name} (${channel.guild.id}/${channel.id})`);
const logChannel = discord.guilds.cache.get(config.discord.mainGuild).channels.cache.get(config.discord.logChannel);
logChannel.send({
embeds: [
{
title: "Failed to send message",
description: `There is likely an issue with permissions. Please notify the server owner if possible.
Guild: ${channel.guild.name} (${channel.guild.id})
Channel: ${channel.name} (${channel.id})
Guild Owner: <@${channel.guild.ownerId}> (${channel.guild.ownerId})
Sub Info: \`\`\`json\n${JSON.stringify(row)}\`\`\``,
color: 0xff0000
}
]
});
discord.users.fetch(channel.guild.ownerId).then((user) => {
user.send({
embeds: [
{
title: "Issue with your subscribed channel.",
description: `There is likely an issue with permissions. Please check that I can send messages in <#${channel.id}>\nYour subscription has been removed, and you will need to resubscribe to get alerts.`,
color: 0xff0000,
fields: [
{
name: "Guild",
value: `${channel.guild.name} (${channel.guild.id})`
},
{
name: "Channel",
value: `${channel.name} (${channel.id})`
}
]
}
]
}).catch((err) => {
console.log(`${colors.red("[ERROR]")} Failed to send message to ${channel.guild.ownerId}`);
}).then(() => {
if (channel.guildId == config.discord.mainGuild) return;
db.run(`DELETE FROM channels WHERE channelid = ? AND iemchannel = ?`, [channel.id, fromChannel], (err) => {
if (err) {
console.error(err.message);
}
console.log(`${colors.cyan("[INFO]")} Deleted channel ${channel.id} from database`);
});
})
});
}); });
}); });
}).catch((err) => { }).catch((err) => {
@ -479,6 +577,27 @@ xmpp.on("stanza", (stanza) => {
thisMsg.content = row.custommessage || null; thisMsg.content = row.custommessage || null;
user.send(thisMsg).catch((err) => { user.send(thisMsg).catch((err) => {
console.error(err); console.error(err);
}).catch((err) => {
console.log(`${colors.yellow("[WARN]")} Failed to send message to ${user.tag} (${user.id})`);
const logChannel = discord.guilds.cache.get(config.discord.mainGuild).channels.cache.get(config.discord.logChannel);
logChannel.send({
embeds: [
{
title: "Failed to send DM",
description: `User may have DMs disabled, or bot doesnt' share a server anymore!.
User: ${user.tag} (${user.id})
Sub Info: \`\`\`json\n${JSON.stringify(row)}\`\`\``,
color: 0xff0000
}
]
}).then(() => {
db.run(`DELETE FROM userAlerts WHERE userid = ? AND iemchannel = ?`, [user.id, fromChannel], (err) => {
if (err) {
console.error(err.message);
}
console.log(`${colors.cyan("[INFO]")} Deleted user ${user.id} from database`);
});
})
}); });
}); });
}).catch((err) => { }).catch((err) => {
@ -585,20 +704,19 @@ discord.on('ready', async () => {
commands = require("./data/commands.json"); commands = require("./data/commands.json");
// Add dynamic commands (based on datas files) // Add dynamic commands (based on datas files)
satCommand = { satCommand = {
"name": "sattelite", "name": "satellite",
"description": "Get the latest sattelite images from a given sattelite", "description": "Get the latest satellite images from a given satellite",
"options": [ "options": [
{ {
"name": "sattelite", "name": "satellite",
"description": "The sattelite to get images from", "description": "The satellite to get images from",
"type": 3, "type": 3,
"required": true, "required": true,
"choices": [] "choices": []
} }
] ]
} }
for (const key in sattelites) { for (const key in satellites) {
// Push the key to the choices array
satCommand.options[0].choices.push({ satCommand.options[0].choices.push({
"name": key, "name": key,
"value": key "value": key
@ -673,22 +791,16 @@ discord.on('ready', async () => {
"description": "The URL of the stream to play", "description": "The URL of the stream to play",
"type": 3, "type": 3,
"required": true, "required": true,
"choices": [] "autocomplete": true
} }
] ]
} }
for (const key in nwrstreams.callsigns) {
nwrplayCommand.options[0].choices.push({
"name": key,
"value": key
});
}
commands.push(nwrplayCommand); commands.push(nwrplayCommand);
} }
await (async () => { await (async () => {
try { try {
//Global //Global
if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} Registering global commands: ${JSON.stringify(commands, null, 2)}`); if (config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} Registering global commands`);
await rest.put(Routes.applicationCommands(discord.user.id), { body: commands }) await rest.put(Routes.applicationCommands(discord.user.id), { body: commands })
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -753,21 +865,26 @@ discord.on("interactionCreate", async (interaction) => {
filterEvt = interaction.options.getString("filterevt") || null; filterEvt = interaction.options.getString("filterevt") || null;
message = interaction.options.getString("message") || null; message = interaction.options.getString("message") || null;
if (interaction.inGuild()) { if (interaction.inGuild()) {
db.get(`SELECT * FROM channels WHERE channelid = ? AND iemchannel = ?`, [interaction.channel.id, room], (err, row) => { interaction.channel.send("Permission check").then((msg) => {
if (err) { msg.delete();
console.error(err.message); db.get(`SELECT * FROM channels WHERE channelid = ? AND iemchannel = ?`, [interaction.channel.id, room], (err, row) => {
interaction.reply({ content: "Failed to subscribe to room", ephemeral: true });
} else if (row) {
return interaction.reply({ content: `Already subscribed to \`${getWFOByRoom(room).location}\`\nIf you want to update a subscribtion, please unsubscribe and resubscribe. This will be made a command eventually.`, ephemeral: true });
}
db.run(`INSERT INTO channels (channelid, iemchannel, custommessage, filter, filterevt, minPriority) VALUES (?, ?, ?, ? ,? ,?)`, [interaction.channel.id, room, message, filter, filterEvt, minPriority], (err) => {
if (err) { if (err) {
console.error(err.message); console.error(err.message);
interaction.reply({ content: "Failed to subscribe to room", ephemeral: true }); interaction.reply({ content: "Failed to subscribe to room", ephemeral: true });
} else { } else if (row) {
interaction.reply({ content: `Subscribed to \`${getWFOByRoom(room).location}\``, ephemeral: true }); return interaction.reply({ content: `Already subscribed to \`${getWFOByRoom(room).location}\`\nIf you want to update a subscribtion, please unsubscribe and resubscribe. This will be made a command eventually.`, ephemeral: true });
} }
db.run(`INSERT INTO channels (channelid, iemchannel, custommessage, filter, filterEvt, minPriority) VALUES (?, ?, ?, ? ,? ,?)`, [interaction.channel.id, room, message, filter, filterEvt, minPriority], (err) => {
if (err) {
console.error(err.message);
interaction.reply({ content: "Failed to subscribe to room", ephemeral: true });
} else {
interaction.reply({ content: `Subscribed to \`${getWFOByRoom(room).location}\``, ephemeral: true });
}
});
}); });
}).catch((err) => {
interaction.reply({ content: "Failed to subscribe to room. Bot does not have send message permissions here!", ephemeral: true });
}); });
} else { // We're in a DM } else { // We're in a DM
db.get(`SELECT * FROM userAlerts WHERE userid = ? AND iemchannel = ?`, [interaction.user.id, room], (err, row) => { db.get(`SELECT * FROM userAlerts WHERE userid = ? AND iemchannel = ?`, [interaction.user.id, room], (err, row) => {
@ -891,7 +1008,8 @@ discord.on("interactionCreate", async (interaction) => {
channels = row.count channels = row.count
await getUniqueChannels().then((unique) => { await getUniqueChannels().then((unique) => {
uniques = unique; uniques = unique.channels;
guilds = unique.guilds;
}); });
discord.users.fetch("289884287765839882").then((chrisUser) => { discord.users.fetch("289884287765839882").then((chrisUser) => {
const embed = { const embed = {
@ -916,12 +1034,12 @@ discord.on("interactionCreate", async (interaction) => {
}, },
{ {
name: "Subscribed Rooms", name: "Subscribed Rooms",
value: channels.toLocaleString(), value: `${channels.toLocaleString()}`,
inline: true inline: true
}, },
{ {
name: "Unique Channels", name: "Unique Channels",
value: uniques.toLocaleString(), value: `${uniques.toLocaleString()} in ${guilds} guilds.`,
inline: true inline: true
} }
], ],
@ -1237,48 +1355,42 @@ discord.on("interactionCreate", async (interaction) => {
console.error(err); console.error(err);
}); });
break; break;
case "sattelite": // Get satellite images case "satellite": // Get satellite images
sat = interaction.options.getString("sattelite"); sat = interaction.options.getString("satellite");
if (!sattelites[sat]) return interaction.reply({ content: "Invalid satellite", ephemeral: true }); if (!satellites[sat]) return interaction.reply({ content: "Invalid satellite", ephemeral: true });
// Fetch all the images // Fetch all the images
await interaction.deferReply(); productOptions = []
imageBuffers = {}; await (() => {
embeds = []; for (const key in satellites[sat].products) {
files = []; // make a discord customid safe id for the product name, add it to the satellites object
sattelites[sat].forEach(async (imgData) => { satellites[sat].products[key].customid = key.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
// Get a buffer for the data, and put that in imageBuffers with the "name" as the key productOptions.push({
fetch(imgData.url).then((res) => { label: key,
if (res.status !== 200) { value: satellites[sat].products[key].customid
interaction.editReply({ content: "Failed to get satellite images", ephemeral: true }); })
return; }
} console.log(JSON.stringify(productOptions, null, 2))
res.buffer().then((buffer) => { })();
imageBuffers[imgData.name] = buffer; satMessages[interaction.id] = {
files.push({ sat
attachment: buffer, }
name: `${imgData.name}.jpg` await interaction.reply({
}); content: "Choose a product",
embeds.push({ components: [
title: `${sat} ${imgData.name}`, {
image: { type: 1,
url: `attachment://${imgData.name}.jpg` components: [
{
type: 3,
custom_id: `satproduct|${interaction.id}`,
label: "Product",
// map options to product names
options: productOptions
} }
}); ]
// Check if we have all the images }
if (Object.keys(imageBuffers).length === sattelites[sat].length) { ]
// Send the images })
interaction.editReply({
embeds,
files
});
}
});
}).catch((err) => {
interaction.editReply({ content: "Failed to get satellite images", ephemeral: true });
console.log(`${colors.red("[ERROR]")} Failed to get satellite images: ${err.message}`);
console.error(err);
});
});
break; break;
case "forecast": case "forecast":
@ -1299,51 +1411,198 @@ discord.on("interactionCreate", async (interaction) => {
} }
case Discord.InteractionType.MessageComponent: case Discord.InteractionType.MessageComponent:
if (interaction.customId) { if (!interaction.customId) return;
const product_id = interaction.customId; switch (interaction.customId.split("|")[0]) {
url = `https://mesonet.agron.iastate.edu/api/1/nwstext/${product_id}`; case "product":
await interaction.deferReply({ ephemeral: true }); if (interaction.customId) {
fetch(url).then((res) => { const product_id = interaction.customId.split("|")[1];
if (res.status !== 200) { url = `https://mesonet.agron.iastate.edu/api/1/nwstext/${product_id}`;
interaction.reply({ content: "Failed to get product text", ephemeral: true }); await interaction.deferReply({ ephemeral: true });
return; fetch(url).then((res) => {
if (res.status !== 200) {
interaction.reply({ content: "Failed to get product text", ephemeral: true });
return;
}
// Retruns raw text, paginate it into multiple embeds if needed
res.text().then(async (text) => {
const pages = text.match(/[\s\S]{1,1900}(?=\n|$)/g);
// const embeds = pages.map((page, ind) => ({
// title: `Product Text for ${product_id} Pg ${ind + 1}/${pages.length}`,
// description: `\`\`\`${page}\`\`\``,
// color: 0x00ff00
// }));
const messages = pages.map((page, ind) => {
return `\`\`\`${page}\`\`\``
})
messages.forEach(async (message) => {
interaction.followUp({ content: message, ephemeral: true });
})
});
}).catch((err) => {
interaction.reply({ content: "Failed to get product text", ephemeral: true });
console.log(`${colors.red("[ERROR]")} Failed to get product text: ${err.message}`);
});
} }
// Retruns raw text, paginate it into multiple embeds if needed break;
res.text().then(async (text) => { case "satproduct":
const pages = text.match(/[\s\S]{1,1900}(?=\n|$)/g); satData = satMessages[interaction.customId.split("|")[1]];
// const embeds = pages.map((page, ind) => ({ sat = satData.sat
// title: `Product Text for ${product_id} Pg ${ind + 1}/${pages.length}`, product = interaction.values[0];
// description: `\`\`\`${page}\`\`\``, // find the original product name
// color: 0x00ff00 product_name = Object.keys(satellites[sat].products).find(key => satellites[sat].products[key].customid === product);
// })); imageOptions = []
const messages = pages.map((page, ind) => { satMessages[interaction.customId.split("|")[1]] = {
return `\`\`\`${page}\`\`\`` sat,
}) product,
messages.forEach(async (message) => { product_name,
interaction.followUp({ content: message, ephemeral: true }); images: {}
}) }
await (() => {
// for key, value in satellites[sat].products[product_name]
console.log(product_name)
for (const key in satellites[sat].products[product_name]) {
// make a discord customid safe id for the product name, add it to the satellites object
//console.log(satellites[sat].products[product_name])
if (key === "customid") continue;
satMessages[interaction.customId.split("|")[1]].images[key.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()] = satellites[sat].products[product_name][key];
imageOptions.push({
label: key,
value: key.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()
})
}
})();
interaction.deferReply();
await interaction.message.edit({
content: "Choose an image to view",
components: [
{
type: 1,
components: [
{
type: 3,
custom_id: `satproduct2|${interaction.customId.split("|")[1]}`,
label: "Image",
// map options to product names
options: imageOptions
}
]
}
]
}).then(() => {
interaction.deleteReply();
}); });
}).catch((err) => { break;
interaction.reply({ content: "Failed to get product text", ephemeral: true }); case "satproduct2":
console.log(`${colors.red("[ERROR]")} Failed to get product text: ${err.message}`); satData = satMessages[interaction.customId.split("|")[1]];
}); sat = satData.sat;
product = satData.product;
product_name = satData.product_name;
image = interaction.values[0];
url = satData.images[image];
// get filename from url
filename = url.split("/").pop();
interaction.deferReply();
// Get the image
fetch(url).then((res) => {
if (res.status !== 200) {
interaction.message.edit({ content: "Failed to get image", ephemeral: true }).then(() => {
interaction.deleteReply();
});
return;
}
embeds = [];
files = [];
res.buffer().then(async (buffer) => {
files.push({
attachment: buffer,
name: filename
})
embeds.push({
title: `${sat}/${product_name}/${image}`,
image: {
url: `attachment://${filename}`
},
color: 0x00ff00
});
interaction.message.edit({
embeds,
files,
components: [],
content: null
}).then(() => {
interaction.deleteReply();
});
}
);
}).catch((err) => {
interaction.message.edit({ content: "Failed to get image", ephemeral: true }).then(() => {
interaction.deleteReply();
});
console.log(`${colors.red("[ERROR]")} Failed to get image: ${err.stack}`);
});
break;
} }
break; break;
case Discord.InteractionType.ApplicationCommandAutocomplete:
//map nwrstreams
if (interaction.commandName === "nwrplay") {
let callsignSearch = interaction.options.getString("callsign");
let callsigns = Object.keys(nwrstreams.callsigns);
let results = callsigns.filter((callsign) => callsign.toLowerCase().includes(callsignSearch.toLowerCase()));
if (results.length > 25) {
results = results.slice(0, 25);
}
interaction.respond(results.map((callsign) => ({ name: callsign, value: callsign })));
}
break;
} }
}); });
discord.on("guildCreate", (guild) => { discord.on("guildCreate", async (guild) => {
let logs = await guild.fetchAuditLogs()
logs = logs.entries.filter(e => e.action === Discord.AuditLogEvent.BotAdd)
let user = logs.find(l => l.target?.id === discord.user.id)?.executor
// Get the main guild // Get the main guild
const myGuild = discord.guilds.cache.get(config.discord.mainGuild); const myGuild = discord.guilds.cache.get(config.discord.mainGuild);
// Get the log channel // Get the log channel
const channel = myGuild.channels.cache.get(config.discord.logChannel); const channel = myGuild.channels.cache.get(config.discord.logChannel);
// Send a message to the log channel // Send a message to the log channel
let invite = await discord.guilds.cache.get(config.discord.mainGuild).channels.cache.get(config.discord.inviteChannel).createInvite();
user.send({
embeds: [{
description: `Thanks for adding ${discord.user.username}!\nIf you have **ANY** questions, comments, suggestions, bug reports, etc, please feel free to throw it by us in our support server!\n\nTo get started use \`/subscribe\` to get alerts!`,
color: 0x00ff00
}],
components: [
{
type: Discord.ComponentType.ActionRow,
components: [
{
type: Discord.ComponentType.Button,
url: `https://discord.gg/${invite.code}`,
style: Discord.ButtonStyle.Link,
emoji: "",
label: "IEM Alerter Support Server"
}
]
}
]
}).catch((err) => {
console.log(`${colors.red("[ERROR]")} Failed to send message to user ${user.id}: ${err.message}`);
})
channel.send({ channel.send({
embeds: [ embeds: [
{ {
description: `I joined \`${guild.name}\``, description: `I joined \`${guild.name}\``,
fields: [
{
"name": "User",
"value": `<@${user.id}> (@${user.username}) ${user.displayName}`
}
],
color: 0x00ff00 color: 0x00ff00
} }
] ]
@ -1368,7 +1627,7 @@ discord.on("guildDelete", (guild) => {
}) })
process.on("unhandledRejection", (error, promise) => { process.on("unhandledRejection", (error, promise) => {
console.log(`${colors.red("[ERROR]")} Unhandled Rejection @ ${promise}: ${error}`); console.log(`${colors.red("[ERROR]")} Unhandled Rejection @ ${promise}: ${error.stack}`);
// create errors folder if it doesnt exist // create errors folder if it doesnt exist
if (!fs.existsSync("./error")) { if (!fs.existsSync("./error")) {
fs.mkdirSync("./error"); fs.mkdirSync("./error");
@ -1411,5 +1670,7 @@ process.on("uncaughtException", (error) => {
return; return;
}); });
// Login to discord // Login to discord
discord.login(config.discord.token); discord.login(config.discord.token);

View file

@ -14,7 +14,7 @@
"@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.js": "^14.15.2", "discord.js": "14.14.1",
"geolib": "^3.3.4", "geolib": "^3.3.4",
"html-entities": "^2.5.2", "html-entities": "^2.5.2",
"jimp": "^0.22.12", "jimp": "^0.22.12",