diff --git a/data/nwrstreams.json b/data/nwrstreams.json new file mode 100644 index 0000000..af00e55 --- /dev/null +++ b/data/nwrstreams.json @@ -0,0 +1,12 @@ +{ + "callsigns": { + "WXL46": "https://icecast.sirenarchive.xyz/NWR/WXL46", + "KZZ30": "https://icecast.sirenarchive.xyz/NWR/KZZ30", + "KZZ57": "https://wxradio.org/IL-Rockford-KZZ57", + "KXI41": "https://wxradio.org/IL-CrystalLake-KXI41", + "KHB34": "https://wxradio.org/FL-Miami-KHB34", + "WNG663": "https://wxradio.org/FL-Princeton-WNG663", + "KEC80": "https://wxradio.org/GA-Atlanta-KEC80", + "KGRX_NOT_NWR": "https://icecast.sirenarchive.xyz/AAC/KGRX" + } +} \ No newline at end of file diff --git a/index.js b/index.js index 1dc6ce9..ad886f9 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ 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"); @@ -30,6 +31,7 @@ const rest = new REST({ version: '10' }).setToken(config.discord.token); + // Setup SQlite DB const db = new sqlite3.Database("channels.db", (err) => { if (err) { @@ -630,6 +632,7 @@ discord.on('ready', async () => { }, { "name": "play", + "type": 1, "description": "Play a stream", "options": [ { @@ -658,10 +661,33 @@ discord.on('ready', async () => { ] } ) + + nwrplayCommand = { + "name": "nwrplay", + "description": "Nwr stream", + "type": 1, + "options": [ + { + "name": "callsign", + "description": "The URL of the stream to play", + "type": 3, + "required": true, + "choices": [] + } + ] + } + for (const key in nwrstreams.callsigns) { + nwrplayCommand.options[0].choices.push({ + "name": key, + "value": key + }); + } + commands.push(nwrplayCommand); } await (async () => { try { //Global + if(config.debug >= 1) console.log(`${colors.magenta("[DEBUG]")} Registering global commands: ${JSON.stringify(commands, null, 2)}`); await rest.put(Routes.applicationCommands(discord.user.id), { body: commands }) } catch (error) { console.error(error); @@ -869,7 +895,7 @@ discord.on("interactionCreate", async (interaction) => { const embed = { title: "About Me!", thumbnail: { - url: discord.user.avatarURL() + url: discord.user?.avatarURL() }, description: `I listen to all the weather.im rooms and send them to discord channels.\nI am open source, you can find my code [here!](https://github.com/ChrisChrome/iembot-2.0)\n\nThis bot is not affiliated with NOAA, the National Weather Service, or the IEM project.`, fields: [ @@ -900,7 +926,7 @@ discord.on("interactionCreate", async (interaction) => { color: 0x00ff00, footer: { text: "Made by @chrischrome with <3", - icon_url: discord.users.cache.get("289884287765839882").avatarURL() + icon_url: "https://cdn.discordapp.com/avatars/289884287765839882/2b00063a92adfe08f325353eee29e348.webp?size=1024&format=webp&width=0&height=256" } } interaction.reply({ embeds: [embed] }); @@ -1000,7 +1026,7 @@ discord.on("interactionCreate", async (interaction) => { break; case "playbcfy": // Play broadcastify stream - if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); + if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); if (!config.broadcastify.enabled) return interaction.reply({ content: "Broadcastify is not enabled", ephemeral: true }); streamID = interaction.options.getString("id"); // Check if the stream ID is valid (up to 10 digit alphanumeric) @@ -1008,7 +1034,7 @@ discord.on("interactionCreate", async (interaction) => { // Get the stream URL url = `https://${config.broadcastify.username}:${config.broadcastify.password}@audio.broadcastify.com/${streamID}.mp3`; // Get the channel - channel = interaction.member.voice.channel; + channel = interaction.member.voice?.channel; if (!channel) return interaction.reply({ content: "You need to be in a voice channel", ephemeral: true }); // Join the channel and play the stream res = JoinChannel(channel, url, .1, interaction) @@ -1020,16 +1046,16 @@ discord.on("interactionCreate", async (interaction) => { break; case "play": // Play generic stream - if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); - // Get the URL - url = interaction.options.getString("url"); - // Sanity check URL for funny stuff - if (!url.match(/https?:\/\/[^\s]+/)) return interaction.reply({ content: "Invalid URL", ephemeral: true }); + if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); + // Use local variables & Get the URL + interactionUrl = interaction.options.getString("url"); // Get the channel - channel = interaction.member.voice.channel; + channel = interaction.member.voice?.channel; + // Check if in channel if (!channel) return interaction.reply({ content: "You need to be in a voice channel", ephemeral: true }); // Join the channel and play the stream - st = JoinChannel(channel, url, .1, interaction) + st = JoinChannel(channel, interactionUrl, .1, interaction); + if (st) { interaction.reply({ content: "Joined, trying to start playing.", ephemeral: true }); } else { @@ -1037,7 +1063,29 @@ discord.on("interactionCreate", async (interaction) => { } break; - case "leave": // Leaves Channel + case "nwrplay": // Play NWR stream + if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); + + // Get the callsign + const callsign = interaction.options.getString("callsign"); + // Get the URL associated with the callsign + const url = nwrstreams.callsigns[callsign]; + + // Get the voice channel + channel = interaction.member.voice?.channel; // Use a unique variable name + if (!channel) return interaction.reply({ content: "You need to be in a voice channel", ephemeral: true }); + + // Join the channel and play the stream + const streamStatus = JoinChannel(channel, url, .1, interaction); // Use a unique variable name + if (streamStatus) { + interaction.reply({ content: "Joined, trying to start playing.", ephemeral: true }); + } else { + interaction.reply({ content: `Failed to play stream`, ephemeral: true }); + } + break; + + + case "leave": // Leave Channel if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); channel = interaction.member.voice.channel; if (!channel) return interaction.reply({ content: "You need to be in a voice channel", ephemeral: true }); @@ -1050,7 +1098,7 @@ discord.on("interactionCreate", async (interaction) => { break; case "pause": // Pause/unpause stream - if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); + if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); channel = interaction.member.voice.channel; if (!channel) return interaction.reply({ content: "You need to be in a voice channel", ephemeral: true }); res = toggleVoicePause(channel) @@ -1061,7 +1109,7 @@ discord.on("interactionCreate", async (interaction) => { } break; case "volume": // Set volume - if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); + if (!interaction.inGuild()) return interaction.reply({ content: "This command can only be used in a guild", ephemeral: true }); channel = interaction.member.voice.channel; if (!channel) return interaction.reply({ content: "You need to be in a voice channel", ephemeral: true }); volume = interaction.options.getInteger("volume") / 100; @@ -1158,9 +1206,9 @@ discord.on("interactionCreate", async (interaction) => { }); break; case "alertmap": - url = "https://forecast.weather.gov/wwamap/png/US.png" + const alertmapurl = "https://forecast.weather.gov/wwamap/png/US.png" await interaction.deferReply(); - fetch(url).then((res) => { + fetch(alertmapurl).then((res) => { if (res.status !== 200) { interaction.editReply({ content: "Failed to get alert map", ephemeral: true }); return; @@ -1170,7 +1218,7 @@ discord.on("interactionCreate", async (interaction) => { embeds: [{ title: `Alert Map`, image: { - url: `attachment://alerts.png` + alertmapurl: `attachment://alerts.png` }, color: 0x00ff00 }],