diff --git a/.gitignore b/.gitignore index 656811d..1f83f4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules data config.js -package-lock.json \ No newline at end of file +package-lock.json +.env \ No newline at end of file diff --git a/README.md b/README.md index d0fde98..221d3e7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ # Woomy Woomy is a all-purpose discord bot built off the [guidebot](https://github.com/AnIdiotsGuide/guidebot) base and coded in node.js using discord.js. -# How to use -The easiest way to use Woomy is to invite it to your server with [this link.](https://discordapp.com/oauth2/authorize?client_id=435961704145485835&permissions=2134240503&scope=bot) It is hosted 24/7 and automatically updates itself when a new release is made available, making sure you always get the newest features. +# Notice +Woomy v1 (this repository) is currently only being maintained. Bugs and issues will be fixed if they come up, but no new features will be added. Pull requests that add new features to Woomy should instead be contributed to the version 2 codebase, found [here](https://github.com/woomyware/v2). -You can also self-host! Some modificatiomns to the code will need to be made before Woomy will run on your machine, but anyone who can read errors will figure out what needs to be changed pretty quickly :P +When Woomy v2 is released, Woomy v1 will no longer be maintained. + +# How to use +The easiest way to use Woomy is to invite it to your server with [this link.](https://discord.com/oauth2/authorize?client_id=435961704145485835&permissions=2134240503&scope=bot) It is hosted 24/7 and automatically updates itself when a new release is made available, making sure you always get the newest features. + +You can also self-host! Some modifications to the code will need to be made before Woomy will run on your machine, but anyone who can read errors will figure out what needs to be changed pretty quickly :P # Requirements - git diff --git a/configTemplate.js b/configTemplate.js index b6e96c3..7480756 100644 --- a/configTemplate.js +++ b/configTemplate.js @@ -11,8 +11,14 @@ const config = { // Tokens "token": "", // Your bot's token. "devtoken": "", // (optional) another token, meant for a bot used for development - "ytkey": "", // Youtube API key, needed for music searching to work - "dblkey": "", // top.gg key, sends bot statistics to top.gg. You do not need this. + "dblkey": "", // (optional) top.gg key, sends bot statistics to top.gg. You do not need this. + "sentry": "", + "server": "", + + // Configurable API endpoints + endpoints: { + invidious: '' + }, // Default per-server settings "defaultSettings" : { diff --git a/index.js b/index.js index 49d5326..13103d4 100644 --- a/index.js +++ b/index.js @@ -6,8 +6,17 @@ const Discord = require('discord.js'); const { promisify } = require('util'); const readdir = promisify(require('fs').readdir); const Enmap = require('enmap'); +const sentry = require('@sentry/node'); const chalk = require('chalk'); -const client = new Discord.Client(); +const client = new Discord.Client({ ws: { intents: [ + 'GUILDS', + 'GUILD_MEMBERS', + 'GUILD_EMOJIS', + 'GUILD_VOICE_STATES', + 'GUILD_MESSAGES', + 'DIRECT_MESSAGES', + 'GUILD_MESSAGE_REACTIONS', +]}}); try { client.config = require('./config'); @@ -47,6 +56,10 @@ if(client.config.devmodeEnabled == true && process.env['USER'] != 'container') { const DBL = require("dblapi.js"); const dblapi = new DBL(client.config.dblkey, client); }; + + if(client.config.sentry.length > 0) { + sentry.init({ dsn: client.config.sentry }); + }; }; client.commands = new Enmap(); @@ -90,4 +103,10 @@ const init = async () => { }; }; -init(); \ No newline at end of file +process.on('SIGINT', function(){ + client.logger.info("Disconnecting...") + client.destroy(); + process.exit(); +}); + +init(); diff --git a/package.json b/package.json index 891ab34..55a183a 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,31 @@ { "name": "woomy", - "version": "1.1.0", + "version": "1.2.3", "description": "Woomy is a all-purpose discord bot built off the guidebot base and coded in node.js using discord.js.", "main": "index.js", "dependencies": { - "@discordjs/opus": "^0.1.0", - "better-sqlite3": "^5.4.1", - "chalk": "^3.0.0", - "dblapi.js": "^2.3.1", - "discord.js": "^12.0.2", - "enmap": "^5.2.4", - "garfield": "^1.1.2", - "get-youtube-id": "^1.0.1", + "@discordjs/opus": "^0.6.0", + "@sentry/node": "^6.12.0", + "better-sqlite3": "^7.4.0", + "chalk": "^4.1.1", + "dblapi.js": "^2.4.1", + "discord-paginator.js": "git+https://gitdab.com/embee/discord-paginator.js", + "discord.js": "^12.5.3", + "enmap": "^5.8.5", + "ffmpeg-static": "^4.3.0", "hastebin-gen": "^2.0.5", - "moment": "^2.24.0", + "moment": "^2.29.1", "moment-duration-format": "^2.3.2", "nekos.life": "^2.0.5", - "node-fetch": "^2.6.0", - "openweather-apis": "^4.2.0", - "prism-media": "^1.2.1", - "randomcolor": "^0.5.4", + "node-fetch": "^2.6.1", + "pretty-ms": "^7.0.1", + "randomcolor": "^0.6.2", "relevant-urban": "^2.0.0", - "request": "^2.88.2", "to-zalgo": "^1.0.1", "urban": "^0.3.2", "weather-js": "^2.0.0", - "youtube-info": "^1.3.2", - "ytdl-core-discord": "^1.1.0" + "ytdl-core": "^4.9.1" }, - "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/resources/other/lyrics.json b/resources/other/lyrics.json index cd753d2..088dc72 100644 --- a/resources/other/lyrics.json +++ b/resources/other/lyrics.json @@ -15,19 +15,19 @@ "If I'm not back again this time tomorrow", "Carry on, carry on as if nothing really matters", "Too late, my time has come", - "sends shivers down my spine, body's aching all the time", + "Sends shivers down my spine, body's aching all the time", "Goodbye, everybody, I've got to go", "Gotta leave you all behind and face the truth", "Mama, ooh, (Anyway the wind blows)", "I don't wanna die", "I sometimes wish I'd never been born at all", - "i see a little silhouetto of a man", + "I see a little silhouetto of a man", "Scaramouche! Scaramouche! will you do the Fandango?", "Thunderbolt and lightning, very, very fright'ning me", "(Galileo) Galileo, (Galileo) Galileo, Galileo Figaro magnifico", "I'm just a poor boy, nobody loves me", "He's just a poor boy from a poor family", - "spare him his life from this monstrosity", + "Spare him his life from this monstrosity", "Easy come, easy go, will you not let me go?", "Bismillah! No, we will not let you go", "(Let him go!) Bismillah! We will not let you go", @@ -35,17 +35,17 @@ "(Let me go) Will not let you go", "(Let me go) Will not let you go", "(Let me go) Ah", - "no, no, no, no, no, no, no", + "No, no, no, no, no, no, no", "(Oh mamma mia, mamma mia) Mamma mia, let me go", "Beelzebub has the devil put aside for me, for me, for me!", "So you think you can stone me and spit in my eye?", - "so you think you can love me and leave me to die?", + "So you think you can love me and leave me to die?", "Oh baby, can't do this to me, baby!", "Just gotta get out, just gotta get right outta here!", "Nothing really matters, anyone can see", - "nothing really matters", + "Nothing really matters", "Nothing really matters, to me", - "any way the wind blows" + "Any way the wind blows" ], "creeper": [ diff --git a/src/commands/blacklist.js b/src/commands/blocklist.js similarity index 76% rename from src/commands/blacklist.js rename to src/commands/blocklist.js index 20c41aa..f696217 100644 --- a/src/commands/blacklist.js +++ b/src/commands/blocklist.js @@ -7,7 +7,7 @@ exports.run = async (client, message, [action, ...member]) => { if(!action) { return message.channel.send( - `<:error:466995152976871434> You didn't tell me if I was meant to add or remove someone from the blacklist! Usage: \`${client.commands.get(`blacklist`).help.usage}\`` + `<:error:466995152976871434> You didn't tell me if I was meant to add or remove someone from the blocklist! Usage: \`${client.commands.get(`blocklist`).help.usage}\`` ) } @@ -15,7 +15,7 @@ exports.run = async (client, message, [action, ...member]) => { if(!member) { return message.channel.send( - `<:error:466995152976871434> You didn't tell me who to blacklist! Usage: \`${client.commands.get(`blacklist`).help.usage}\`` + `<:error:466995152976871434> You didn't tell me who to add to the blocklist! Usage: \`${client.commands.get(`blocklist`).help.usage}\`` ); }; @@ -41,18 +41,18 @@ exports.run = async (client, message, [action, ...member]) => { }; if (user.id === message.guild.owner.id) { - return message.channel.send("<:error:466995152976871434> You can't blacklist the owner!") + return message.channel.send("<:error:466995152976871434> You can't add the owner to the blocklist!") }; let admin = message.guild.member(message.author) if (user.roles.highest.position >= admin.roles.highest.position && admin.user.id !== message.guild.ownerID) { return message.channel.send( - `<:error:466995152976871434> You can't blacklist people higher ranked than yourself!` + `<:error:466995152976871434> You can't add people higher ranked than yourself to the blocklist!` ); }; if(user.id === message.member.id) { - return message.channel.send('<:error:466995152976871434> You can\'t blacklist yourself!'); + return message.channel.send('<:error:466995152976871434> You can\'t add yourself to the blocklist!'); }; let blacklisted = false; @@ -65,13 +65,13 @@ exports.run = async (client, message, [action, ...member]) => { }); if(blacklisted == true) { - return message.channel.send('<:error:466995152976871434> This person has already been blacklisted!'); + return message.channel.send('<:error:466995152976871434> This person is already on the blocklist!'); }; }; client.settings.push(message.guild.id, user.id, "blacklisted") - return message.channel.send(`<:success:466995111885144095> Blacklisted \`${user.user.tag}\``) + return message.channel.send(`<:success:466995111885144095> Added \`${user.user.tag}\` to the blocklist.`) }; @@ -99,26 +99,26 @@ exports.run = async (client, message, [action, ...member]) => { }); if(blacklisted != true) { - return message.channel.send('<:error:466995152976871434> This user isn\'t blacklisted!'); + return message.channel.send('<:error:466995152976871434> This user isn\'t on the blocklist!'); }; client.settings.remove(message.guild.id, user.id, "blacklisted") - return message.channel.send(`<:success:466995111885144095> Removed \`${user.user.tag}\` from the blacklist.`) + return message.channel.send(`<:success:466995111885144095> Removed \`${user.user.tag}\` from the blocklist.`) }; }; exports.conf = { enabled: true, guildOnly: true, - aliases: [], + aliases: ['bl'], permLevel: "Administrator", requiredPerms: [] }; exports.help = { - name: "blacklist", + name: "blocklist", category: "Moderation", - description: "Allows you to configure Woomy's blacklist. Blacklisted users cannot use commands.", - usage: "blacklist [add/remove] [member]" + description: "Allows you to configure Woomy's blocklist. Users on the blocklist cannot use commands.", + usage: "blocklist [add/remove] [member]" }; diff --git a/src/commands/bohemian_rhapsody.js b/src/commands/bohemian_rhapsody.js index c9bb3ef..878b8e9 100644 --- a/src/commands/bohemian_rhapsody.js +++ b/src/commands/bohemian_rhapsody.js @@ -40,6 +40,6 @@ exports.conf = { exports.help = { name: "bohemian_rhapsody", category: "Fun", - description: "Queen kareoke", + description: "Queen karaoke", usage: "bohemian_rhapsody" }; diff --git a/src/commands/calculate.js b/src/commands/calculate.js index e7737b3..2ebab0b 100644 --- a/src/commands/calculate.js +++ b/src/commands/calculate.js @@ -1,42 +1,46 @@ -var allowed = ["+", "-", "*", "/", "(", ")", " "]; -exports.run = (client, message, args) => { - let exercise = args.join(" "); - - if (!exercise) { - return message.channel.send( - `<:error:466995152976871434> No equation provided. Usage :\`${client.commands.get(`calculate`).help.usage}\`` - ); - } - - for (var i = 0; i < exercise.length; i++) { - let c = exercise.charAt(i); - let found = allowed.find((element) => element === c); - - if(c == "0") found = true; - if(!(Number(c) || found)) - { - return message.channel.send( - `<:error:466995152976871434> Invalid equation. Please use \`*\` for multiplication and \`/\` for division!` - ); - } - } - - let result = (new Function( 'return ' + exercise )()); + var allowed = ["+", "-", "*", "/", "(", ")", " "]; + exports.run = (client, message, args) => { + let exercise = args.join(" "); + + if (!exercise) { + return message.channel.send( + `<:error:466995152976871434> No equation provided. Usage :\`${client.commands.get(`calculate`).help.usage}\`` + ); + } - message.channel.send(`\`RESULT:\`\n\`\`\`${result}\`\`\``); -}; + try { + for (var i = 0; i < exercise.length; i++) { + let c = exercise.charAt(i); + let found = allowed.find((element) => element === c); + + if(c == "0") found = true; + if(!(Number(c) || found)) + { + return message.channel.send( + `<:error:466995152976871434> Invalid equation. Please use \`*\` for multiplication and \`/\` for division!` + ); + } + } + + let result = (new Function( 'return ' + exercise )()); -exports.conf = { - enabled: true, - guildOnly: false, - aliases: ["calc", "math"], - permLevel: "User", - requiredPerms: [] -}; + message.channel.send(`\`RESULT:\`\n\`\`\`${result}\`\`\``) + } catch (err) { + message.channel.send('<:error:466995152976871434> Malformed input.') + } + }; -exports.help = { - name: "calculate", - category: "Utility", - description: "Solves basic mathematical equations.", - usage: "calculate [equation]" -}; \ No newline at end of file + exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["calc", "math"], + permLevel: "User", + requiredPerms: [] + }; + + exports.help = { + name: "calculate", + category: "Utility", + description: "Solves basic mathematical equations.", + usage: "calculate [equation]" + }; \ No newline at end of file diff --git a/src/commands/catfact.js b/src/commands/catfact.js index 366999e..c190cd0 100644 --- a/src/commands/catfact.js +++ b/src/commands/catfact.js @@ -5,6 +5,9 @@ exports.run = async (bot, message, args) => { fetch('https://catfact.ninja/facts') .then(res => res.json()) .then(json => message.channel.send(`__**Did you know?**__\n${json.data[0].fact}`)) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); } catch(err) { message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); }; diff --git a/src/commands/coinflip.js b/src/commands/coinflip.js index 418a996..4ce5b4a 100644 --- a/src/commands/coinflip.js +++ b/src/commands/coinflip.js @@ -1,15 +1,4 @@ exports.run = (client, message, args) => { - if(!args[0]) { - return message.channel.send( - `<:error:466995152976871434> Invalid choice. Usage: \`${client.commands.get(`flip`).help.usage}\`` - ); - }; - - if(args[0].toLowerCase() != "heads" && args[0].toLowerCase() != "tails") { - return message.channel.send( - `<:error:466995152976871434> Invalid choice. Usage: \`${client.commands.get(`flip`).help.usage}\`` - ); - }; var coin = [ "Heads!", "Tails!" @@ -31,5 +20,5 @@ exports.help = { name: "coinflip", category: "Fun", description: "Flips a coin!", - usage: "coinflip [heads/tails]" + usage: "coinflip" }; diff --git a/src/commands/colour.js b/src/commands/colour.js index bdcbf61..776e463 100644 --- a/src/commands/colour.js +++ b/src/commands/colour.js @@ -12,7 +12,7 @@ exports.run = async (client, message, args, level) => { embed = new Discord.MessageEmbed(); embed.setTitle(colour) embed.setColor(colour); - embed.setImage("https://api.alexflipnote.xyz/colour/image/" + colour.replace("#", "")); + embed.setImage(`https://fakeimg.pl/256x256/${colour.replace("#", "")}/?text=%20`); message.channel.send(embed) }; diff --git a/src/commands/dogfact.js b/src/commands/dogfact.js index fca8a62..d962a12 100644 --- a/src/commands/dogfact.js +++ b/src/commands/dogfact.js @@ -4,7 +4,10 @@ exports.run = async (bot, message, args) => { try{ fetch('https://dog-api.kinduff.com/api/facts') .then(res => res.json()) - .then(json => message.channel.send(`__**Did you know?**__\n ${json.facts[0]}`)); + .then(json => message.channel.send(`__**Did you know?**__\n ${json.facts[0]}`)) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); } catch(err) { message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); }; diff --git a/src/commands/fixmusic.js b/src/commands/fixmusic.js new file mode 100644 index 0000000..fb260db --- /dev/null +++ b/src/commands/fixmusic.js @@ -0,0 +1,75 @@ +const { getGuild } = require('../modules/music') +module.exports.run = async (client, message, args, level) =>{ + guild = getGuild(message.guild.id) + + const lvl = client.config.permLevels.find(l => l.level === level) + + if (lvl.level >= 1) { + guild.queue = [] + guild.playing = false + guild.paused = false + guild.skippers = [] + guild.fixers = [] + guild.channel = null + + if (guild.dispatcher) { + guild.dispatcher.end('silent') + } + + guild.fixers = [] + + message.channel.send( + '<:success:466995111885144095> Music has been fixed!' + ) + + return + } + + const vc = message.guild.members.cache.get(client.user.id).voice.channel + + if (guild.fixers.indexOf(message.author.id) === -1) { + guild.fixers.push(message.author.id) + + if (guild.fixers.length >= Math.ceil(vc.members.filter(member => !member.user.bot).size / 2)) { + guild.queue = [] + guild.playing = false + guild.paused = false + guild.skippers = [] + guild.fixers = [] + guild.channel = null + + if (guild.dispatcher) { + guild.dispatcher.end('silent') + } + + guild.fixers = [] + + message.channel.send( + '<:success:466995111885144095> Music has been fixed!' + ) + } else { + message.channel.send( + `<:success:466995111885144095> Your vote has been acknowledged! **${guild.fixers.length + '/' + Math.ceil(vc.members.filter(member => !member.user.bot).size / 2)}**` + ) + }; + } else { + message.channel.send( + '<:denied:466995195150336020> You cannot vote twice!' + ) + } +} + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: [] +}; + +exports.help = { + name: "fixmusic", + category: "Music", + description: 'Fixes music if it breaks.', + usage: 'fixmusic', +}; diff --git a/src/commands/forceskip.js b/src/commands/forceskip.js index 7e341d6..0b6c868 100644 --- a/src/commands/forceskip.js +++ b/src/commands/forceskip.js @@ -1,11 +1,18 @@ +const { skip, getGuild } = require('../modules/music') exports.run = (client, message) => { - let guild = client.music.getGuild(message.guild.id); + const guild = getGuild(message.guild.id) - if(guild.queue.length < 1) return message.channel.send( - `<:error:466995152976871434> There is nothing for me to skip!` - ); - skip_song(guild); - message.channel.send("<:skip:467216735356059660> Skipped the song!") + if (guild.queue.length < 1 || !guild.playing || !guild.dispatcher) { + return message.channel.send( + '<:error:466995152976871434> Nothing is playing.' + ) + } + + skip(message.guild, 'skip') + + guild.skippers = [] + + message.channel.send('<:success:466995111885144095> Song skipped.') }; exports.conf = { @@ -13,7 +20,7 @@ exports.conf = { guildOnly: true, aliases: [], permLevel: "Moderator", - requiredPerms: ["SPEAK"] + requiredPerms: [] }; exports.help = { @@ -22,7 +29,3 @@ exports.help = { description: "Skips the currently playing song without requiring a vote.", usage: "forceskip" }; - -function skip_song(guild) { - guild.dispatcher.end(); -} diff --git a/src/commands/foxgirl.js b/src/commands/foxgirl.js deleted file mode 100644 index f3f3312..0000000 --- a/src/commands/foxgirl.js +++ /dev/null @@ -1,30 +0,0 @@ -const API = require('nekos.life'); -const {sfw} = new API(); -exports.run = async (client, message) => { - message.channel.startTyping(); - try { - sfw.foxGirl().then((json) => { - message.channel.send(json.url) - message.channel.stopTyping(); - }); - } catch (err) { - client.logger.error("foxgirl.js: " + err); - message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) - message.channel.stopTyping(); - }; -}; - -exports.conf = { - enabled: true, - guildOnly: false, - aliases: [], - permLevel: "User", - requiredPerms: ["EMBED_LINKS"] -}; - -exports.help = { - name: "foxgirl", - category: "Image", - description: "Sends you pictures of fox girls.", - usage: "foxgirl" -}; diff --git a/src/commands/garfield.js b/src/commands/garfield.js index 5376ded..0c262f7 100644 --- a/src/commands/garfield.js +++ b/src/commands/garfield.js @@ -1,8 +1,26 @@ -const garfield = require("garfield"); +const fetch = require("node-fetch") +const { MessageEmbed } = require('discord.js') exports.run = async (client, message) => { - message.channel.send({ files: [garfield.random()] }).catch(() => message.channel.send( - "<:error:466995152976871434> API didn't respond, try again in a few seconds." - )); + message.channel.startTyping(); + try { + fetch('https://garfield-comics.glitch.me/~SRoMG/?date=xxxx') + .then(res => res.json()) + .then(json => { + const embed = new MessageEmbed() + .setTitle(`${json.data.name} (No. ${json.data.number})`) + .setColor(client.embedColour(message)) + .setURL('https://www.mezzacotta.net/garfield/?comic=' + json.data.number) + .setImage(json.data.image.src); + message.channel.send(embed) + }) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); + message.channel.stopTyping(); + } catch (err) { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; }; exports.conf = { diff --git a/src/commands/help.js b/src/commands/help.js index 5c07963..945eeae 100644 --- a/src/commands/help.js +++ b/src/commands/help.js @@ -53,7 +53,7 @@ exports.run = (client, message, args, level) => { embed.addField( "Invite me", - "[Click here](https://discordapp.com/oauth2/authorize?client_id=435961704145485835&permissions=8&scope=bot) to add me to your server", + "[Click here](https://discord.com/oauth2/authorize?client_id=435961704145485835&permissions=8&scope=bot) to add me to your server", true ); embed.addField( @@ -103,7 +103,7 @@ exports.run = (client, message, args, level) => { embed.addField( "Invite me", - "[Click here](https://discordapp.com/oauth2/authorize?client_id=435961704145485835&permissions=8&scope=bot) to add me to your server", + "[Click here](https://discord.com/oauth2/authorize?client_id=435961704145485835&permissions=8&scope=bot) to add me to your server", true ); embed.addField( diff --git a/src/commands/inspirobot.js b/src/commands/inspirobot.js index 17a028f..f7a5865 100644 --- a/src/commands/inspirobot.js +++ b/src/commands/inspirobot.js @@ -4,7 +4,10 @@ exports.run = async (client, message) => { try { fetch('http://inspirobot.me/api?generate=true') .then(res => res.text()) - .then(body => message.channel.send({files: [new Discord.MessageAttachment(body)]})); + .then(body => message.channel.send({files: [new Discord.MessageAttachment(body)]})) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); message.channel.stopTyping(); } catch (err) { message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) diff --git a/src/commands/invite.js b/src/commands/invite.js index 49cdc86..a730eb6 100644 --- a/src/commands/invite.js +++ b/src/commands/invite.js @@ -1,6 +1,6 @@ exports.run = async (client, message) => { message.channel.send( - `Use this link to invite me to your server:\n` + `Use this link to invite me to your server:\n` ); }; diff --git a/src/commands/kemonomimi.js b/src/commands/kemonomimi.js index 3c4b70e..7d16938 100644 --- a/src/commands/kemonomimi.js +++ b/src/commands/kemonomimi.js @@ -15,7 +15,7 @@ exports.run = async (client, message) => { }; exports.conf = { - enabled: true, + enabled: false, guildOnly: false, aliases: [], permLevel: "User", diff --git a/src/commands/kitsune.js b/src/commands/kitsune.js new file mode 100644 index 0000000..7abe85f --- /dev/null +++ b/src/commands/kitsune.js @@ -0,0 +1,31 @@ +const fetch = require("node-fetch") +exports.run = async (client, message, args) => { + message.channel.startTyping(); + try{ + fetch(`https://purrbot.site/api/img/sfw/kitsune/img/`) + .then(res => res.json()) + .then(json => message.channel.send(json.link)) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); + message.channel.stopTyping(); + } catch(err) { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: false, + guildOnly: false, + aliases: ['foxgirl'], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "kitsune", + category: "Image", + description: "Sends you cute wholesome pictures of foxgirls.", + usage: "kitsune" +}; diff --git a/src/commands/lmgtfy.js b/src/commands/lmgtfy.js new file mode 100644 index 0000000..ffd031c --- /dev/null +++ b/src/commands/lmgtfy.js @@ -0,0 +1,31 @@ +const identities = require ("../../resources/other/identities.json"); +exports.run = async (client, message, args) => { + if (!args[0]) { + return message.channel.send("Missing arguments, please provide me with a query!") + } + + const query = args.join("+") + + let link = ("https://lmgtfy.com/?q=" + query) + + if (message.flags.includes('d')) { + link = "https://lmgtfy.com/?q=" + query + "&pp=1&s=d" + } + + message.channel.send(link) +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: [], + permLevel: "User", + requiredPerms: [] +}; + +exports.help = { + name: "lmgtfy", + category: "Fun", + description: "For when you need to remind someone search engines exist..", + usage: "lmgtfy " +}; diff --git a/src/commands/movehere.js b/src/commands/movehere.js new file mode 100644 index 0000000..61ea0ee --- /dev/null +++ b/src/commands/movehere.js @@ -0,0 +1,33 @@ +const { getGuild } = require('../modules/music') +const Discord = require("discord.js") + +module.exports.run = async (client, message, args, level) =>{ + const guild = getGuild(message.guild.id) + + if (!guild.playing) { + return message.channel.send('<:error:466995152976871434> Nothing is playing.') + } + + if (guild.channel.id === message.channel.id) { + return message.channel.send('<:error:466995152976871434> Music messages are already being sent to this channel.') + } + + guild.channel = message.channel + + message.channel.send('<:success:466995111885144095> Music messages will now be sent to this channel.') +} + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "Moderator", + requiredPerms: [] +}; + +exports.help = { + name: 'movehere', + category: 'Music', + description: 'Moves music related messages to the channel the this command is ran in.', + usage: 'movehere', +}; diff --git a/src/commands/movesong.js b/src/commands/movesong.js new file mode 100644 index 0000000..83c6a7c --- /dev/null +++ b/src/commands/movesong.js @@ -0,0 +1,56 @@ +const { getGuild } = require('../modules/music') +exports.run = async (client, message, args) => { + const queue = getGuild(message.guild.id).queue + + if (queue.length < 3) { + return message.channel.send('<:error:466995152976871434> Not enough songs are in the queue for this command to work!') + } + + if (!args[0]) { + return client.userError(message, exports, 'Missing argument, the `current position` argument is required!') + } + + if (!args[1]) { + return client.userError(message, exports, 'Missing argument, the `new position` argument is required!') + } + + const oldPosition = +args[0] + const newPosition = +args[1] + + if (isNaN(oldPosition) === true) { + return message.channel.send('<:error:466995152976871434> That isn\'t a number! You need to tell me the songs position in the queue (1, 2, etc.)') + } + + if (isNaN(newPosition) === true) { + return message.channel.send('<:error:466995152976871434> That isn\'t a number! You need to tell me the songs position in the queue (1, 2, etc.)') + } + + if (oldPosition < 1 || oldPosition >= queue.length) { + return message.channel.send('<:error:466995152976871434> Old position is not a valid song ID.') + } + + if (newPosition < 1 || newPosition >= queue.length) { + return message.channel.send('<:error:466995152976871434> New position is not a valid song ID.') + } + + const songName = queue[oldPosition].video.title + + queue.splice(newPosition, 0, queue.splice(oldPosition, 1)[0]) + + message.channel.send(`<:success:466995111885144095> Moved **${songName}** from position \`${oldPosition}\` to \`${newPosition}\``) +} + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "Moderator", + requiredPerms: [] +} + +exports.help = { + name: 'movesong', + category: 'Music', + description: 'Moves a song to a new position in the queue.', + usage: 'movesong [current position] [new position]' +} \ No newline at end of file diff --git a/src/commands/msearch.js b/src/commands/msearch.js index 876da31..914847b 100644 --- a/src/commands/msearch.js +++ b/src/commands/msearch.js @@ -1,7 +1,7 @@ exports.run = (client, message, args) => { if (!args[0]) return message.channel.send( - `<:error:466995152976871434> No username provided. Usage: \`${client.commands.get(``).help.usage}\`` + `<:error:466995152976871434> No username provided. Usage: \`${client.commands.get(`msearch`).help.usage}\`` ); var mlist = ""; var count = 0; diff --git a/src/commands/neko.js b/src/commands/neko.js index 7ce0d6b..0095ba3 100644 --- a/src/commands/neko.js +++ b/src/commands/neko.js @@ -1,21 +1,22 @@ -const API = require('nekos.life'); -const {sfw} = new API(); -exports.run = async (client, message) => { +const fetch = require("node-fetch") +exports.run = async (client, message, args) => { message.channel.startTyping(); - try { - sfw.neko().then((json) => { - message.channel.send(json.url); + try{ + fetch(`https://purrbot.site/api/img/sfw/neko/img/`) + .then(res => res.json()) + .then(json => message.channel.send(json.link)) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); message.channel.stopTyping(); - }); - } catch (err) { - client.logger.error("neko.js: " + err); - message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + } catch(err) { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); message.channel.stopTyping(); }; }; exports.conf = { - enabled: true, + enabled: false, guildOnly: false, aliases: ["catgirl"], permLevel: "User", @@ -25,6 +26,6 @@ exports.conf = { exports.help = { name: "neko", category: "Image", - description: "Sends you pictures of catgirls.", + description: "Sends you cute wholesome pictures of catgirls.", usage: "neko" }; diff --git a/src/commands/nekogif.js b/src/commands/nekogif.js index 0df6917..122d6de 100644 --- a/src/commands/nekogif.js +++ b/src/commands/nekogif.js @@ -1,21 +1,22 @@ -const API = require('nekos.life'); -const {sfw} = new API(); -exports.run = async (client, message) => { +const fetch = require("node-fetch") +exports.run = async (client, message, args) => { message.channel.startTyping(); - try { - sfw.nekoGif().then((json) => { - message.channel.send(json.url); + try{ + fetch(`https://purrbot.site/api/img/sfw/neko/gif/`) + .then(res => res.json()) + .then(json => message.channel.send(json.link)) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); message.channel.stopTyping(); - }); - } catch (err) { - client.logger.error("nekogif.js: " + err); - message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + } catch(err) { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); message.channel.stopTyping(); }; }; exports.conf = { - enabled: true, + enabled: false, guildOnly: false, aliases: ["catgirlgif"], permLevel: "User", diff --git a/src/commands/nowplaying.js b/src/commands/nowplaying.js index e94f71a..288e51a 100644 --- a/src/commands/nowplaying.js +++ b/src/commands/nowplaying.js @@ -1,31 +1,30 @@ -const Discord = require("discord.js"); +const { getGuild, createTimestamp } = require('../modules/music') +const { MessageEmbed } = require('discord.js') exports.run = async (client, message) => { - let guild = client.music.getGuild(message.guild.id); - - if(guild.queue.length < 1) { - return message.channel.send("<:error:466995152976871434> Nothing is playing."); + const guild = getGuild(message.guild.id) + + if (guild.queue.length < 1) { + return message.channel.send(client.config.emojis.error + ' Nothing is in the queue!') } - var song = guild.queue[0]; - var elapsedTime = client.createTimestamp(guild.dispatcher.streamTime / 1000); - var timestamp; + const s = guild.queue[0] + const elapsedTime = createTimestamp(guild.dispatcher.streamTime / 1000) + let timestamp = `\`[${createTimestamp(s.video.lengthSeconds)}]\`` - if(song.duration == 0) { - timestamp = "`[LIVE]`"; - } else { - timestamp = `\`[${elapsedTime + "/" + client.createTimestamp(song.duration)}]\``; - }; + if (timestamp !== '`[LIVE]`') { + timestamp = `\`[${elapsedTime + '/' + createTimestamp(s.video.lengthSeconds)}]\`` + } - embed = new Discord.MessageEmbed(); - embed.setTitle("Now playing:") - embed.setThumbnail(song.thumbnail) - embed.setColor(client.embedColour(message)); - embed.setDescription(`**[${song.title}](https://www.youtube.com/watch?v=${song.id})**`) - embed.addField("Channel:", song.author, true) - embed.addField("Time:", timestamp, true) - embed.setFooter("Requested by " + song.requestedBy.tag, song.requestedBy.avatarURL({format: "png", dynamic: true, size: 2048})) + const embed = new MessageEmbed() + embed.setTitle('Now playing') + embed.setThumbnail(client.config.endpoints.invidious + s.video.videoThumbnails[1].url) + embed.setColor(client.embedColour(message)) + embed.setDescription(`**[${s.video.title}](https://www.youtube.com/watch?v=${s.video.videoId})**`) + embed.addField('Channel:', s.video.author, true) + embed.addField('Time:', timestamp, true) + embed.setFooter('Requested by ' + s.requestedBy.tag, s.requestedBy.avatarURL({ format: 'png', dynamic: true, size: 2048 })) - message.channel.send(embed) + message.channel.send(embed) }; exports.conf = { diff --git a/src/commands/pause.js b/src/commands/pause.js index 2816baf..e1500e2 100644 --- a/src/commands/pause.js +++ b/src/commands/pause.js @@ -1,15 +1,20 @@ +const { getGuild } = require('../modules/music') exports.run = (client, message, args, level) => { - let guild = client.music.getGuild(message.guild.id); - if(guild.queue.length < 1) { - return message.channel.send("<:error:466995152976871434> Nothing is playing."); - }; + const guild = getGuild(message.guild.id) - guild.playing = false; - guild.paused = true; - guild.dispatcher.pause(); - message.channel.send("<:pause:467639357961142273> Playback paused!"); + if (guild.paused === true) { + return message.channel.send('<:error:466995152976871434> The music has already been paused! Run resume to start the music again.') + } + if (guild.queue.length < 1 || guild.playing === false) { + return message.channel.send('<:error:466995152976871434> Nothing is playing!') + } + guild.playing = false + guild.paused = true + guild.dispatcher.pause() + + message.channel.send('<:pause:467639357961142273> Music playback has been paused.') }; exports.conf = { @@ -17,7 +22,7 @@ exports.conf = { guildOnly: true, aliases: [], permLevel: "Moderator", - requiredPerms: ["CONNECT", "SPEAK"] + requiredPerms: [] }; exports.help = { diff --git a/src/commands/play.js b/src/commands/play.js index d9c8866..a876c63 100644 --- a/src/commands/play.js +++ b/src/commands/play.js @@ -1,20 +1,12 @@ -const util = require("util") +const { play } = require('../modules/music') const Discord = require("discord.js") -module.exports.run = (client, message, args, level) =>{ - if(!args[0]) - { - message.channel.send(`<:error:466995152976871434> You didn't give me a song to play! Usage: \`${client.commands.get(`play`).help.usage}\``); - - return; - } +module.exports.run = async (client, message, args, level) =>{ + if (!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't give me a song name or YouTube URL! Usage: \`${client.commands.get('play').help.usage}\``) + } - let voiceChannel = message.member.voice.channel; - if(!voiceChannel) return message.channel.send('<:error:466995152976871434> You need to be in a voice channel to use this command!'); - - message.channel.send(`šŸ”Ž searching YouTube for \`${args.join(" ")}\``); - - client.music.play(message, args.join(" ")); + await play(client, message, args.join(' '), false) } exports.conf = { @@ -28,6 +20,6 @@ exports.conf = { exports.help = { name: "play", category: "Music", - description: "Plays a song.", - usage: "play [youtube-url] **OR** play [song-name]" + description: 'Plays the song you request, or adds it to the queue.', + usage: 'play [song]', }; diff --git a/src/commands/playnext.js b/src/commands/playnext.js new file mode 100644 index 0000000..6314283 --- /dev/null +++ b/src/commands/playnext.js @@ -0,0 +1,23 @@ +const { play } = require('../modules/music') +exports.run = async (client, message, args) => { + if (!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't give me a song name or YouTube URL! Usage: \`${client.commands.get('play').help.usage}\``) + } + + await play(client, message, args.join(' '), true) +} + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "Moderator", + requiredPerms: [] +} + +exports.help = { + name: 'playnext', + category: 'Music', + description: 'Similar to play, but adds it to the start of the queue instead of the end.', + usage: 'playnext [song]' +} \ No newline at end of file diff --git a/src/commands/pride.js b/src/commands/pride.js new file mode 100644 index 0000000..6a7abfc --- /dev/null +++ b/src/commands/pride.js @@ -0,0 +1,48 @@ +// Copyright 2020 Emily J. / mudkipscience and contributors. Subject to the AGPLv3 license. + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: [], + permLevel: 'User', + requiredPerms: ['ATTACH_FILES'], +} + +exports.help = { + name: 'pride', + category: 'Fun', + description: 'Adds a pride flag ring to your avatar. Available flags are lesbian, gay, bisexual, pansexual, trans, asexual, aromantic and ally.', + usage: '`pride [flag]` - Adds a pride flag overlay to your avatar.\n`pride -g [flag]` - Adds a pride flag gradient on your avatar.', +} + +const url = 'https://demirramon.com/gen/pride.png' +const Discord = require('discord.js') +exports.run = (client, message, args) => { + const flag = args[0] + if (!flag) { + return message.channel.send('<:error:466995152976871434> Missing argument, the `flag` argument is required!') + } + + const available = ['lesbian', 'gay', 'bisexual', 'pansexual', 'trans', 'asexual', 'aromantic', 'ally'] + + if (!available.includes(flag.toLowerCase())) { + return message.channel.send(`<:error:466995152976871434> This flag isn't available, sorry ;~;\nAvailable flags: \`${available.join('`, `')}\``) + } + + let gradient = 'false' + if (message.flags.includes('g')) { + gradient = 'true' + } + + message.channel.startTyping() + + const params = `image=${message.author.avatarURL({ format: 'png', size: 2048 })}&flag=${flag.toLowerCase()}&full=true&gradient=${gradient}&background=false&fit=true&v=2019-08-07` + + try { + message.channel.stopTyping() + message.channel.send({ files: [new Discord.MessageAttachment(url + '?' + params)] }) + } catch (err) { + message.channel.stopTyping() + message.channel.send(`<:error:466995152976871434> Error when generating image: \`${err}\``) + } +} \ No newline at end of file diff --git a/src/commands/queue.js b/src/commands/queue.js index 0bdc87e..7ac4d79 100644 --- a/src/commands/queue.js +++ b/src/commands/queue.js @@ -1,171 +1,162 @@ 'use strict'; -const Discord = require("discord.js"); +const { getGuild, createTimestamp } = require('../modules/music') +const Discord = require('discord.js') exports.run = (client, message, args) => { - var queue = client.music.getGuild(message.guild.id).queue; + var queue = getGuild(message.guild.id).queue - if(queue.length < 1) { - return message.channel.send("<:error:466995152976871434> Nothing is playing."); + if (queue.length < 1) { + return message.channel.send('<:error:466995152976871434> Nothing is playing.') } - let lists = []; + const lists = [] - function generateList(start, number) { - var list = ""; - var timestamp; - var livestream; + function generateList (start, number) { + let list = '' + let timestamp - if(start == 1 && queue.length == 1) { - return ["There's nothing else waiting to be played!", 1]; + if (start === 1 && queue.length === 1) { + return ['There\'s nothing else waiting to be played!', 1] } - if(number == 1 && queue.length + 1 < start) { - return false; - }; - - let q = queue.slice(start); - - let i = 0; - - for(i = 0; i < q.length; i++) { - let song = q[i]; - - if(song.duration == 0) { - timestamp = "LIVE"; - livestream = true; - } else { - timestamp = client.createTimestamp(song.duration); - }; - - let aaa = list + `\`${(i + 1) + start - 1}:\` **[${song.title}](https://www.youtube.com/watch?v=${song.id})** added by ${song.requestedBy} \`[${timestamp}]\`\n`; - - if(aaa.length > 1024) { - return [list, start + i - 1]; - } else { - list = aaa; - } - - //totalDuration = totalDuration + song.duration; - }; - - return [list, start + i + 1]; - }; - - let songsInQueue = queue.length - 1; - let songsInQueueEnglish = "song"; - let timeRemaining = 0; - - function generatePage(list, page) { - if(!list || list == "") { - return false; + if (number === 1 && queue.length + 1 < start) { + return false } - var embed = new Discord.MessageEmbed(); - embed.setTitle(`Queue for: ${message.guild.name}`); - embed.setColor(client.embedColour(message)); - - var elapsedTime = client.music.getGuild(message.guild.id).dispatcher.streamTime / 1000 - var totalDuration = queue[0].duration - elapsedTime; + const q = queue.slice(start) - let timeRemaining = ""; - - for(let i = 1; i < queue.length; i++) { - let b = queue[i]; + let i = 0 - if(b.duration == 0) { - timeRemaining = "āˆž"; + for (i = 0; i < q.length; i++) { + const song = q[i] - break; - } + timestamp = createTimestamp(song.video.lengthSeconds) - totalDuration += b.duration; - } - - if(timeRemaining == "") { - let queueDuration = client.createTimestamp(totalDuration); + const aaa = list + `\`${(i + 1) + start - 1}:\` **[${song.video.title}](https://www.youtube.com/watch?v=${song.video.videoId})** added by ${song.requestedBy} \`[${timestamp}]\`\n` - timeRemaining = queueDuration; - } - - let timestamp; - - if(queue[0].duration == 0) { - timestamp = "LIVE"; - livestream = true; - } else { - timestamp = client.createTimestamp(elapsedTime) + '/' + client.createTimestamp(queue[0].duration); - }; - - embed.addField(`Now playing:`, `**[${queue[0].title}](https://www.youtube.com/watch?v=${queue[0].id})** added by ${queue[0].requestedBy} \`[${timestamp}]\``) - - embed.addField(`Up next:`, list); - - if(songsInQueue > 1 || songsInQueue == 0) { - songsInQueueEnglish = "songs"; - } - - embed.setFooter(`Page ${page}/${lists.length} | ${songsInQueue + " " + songsInQueueEnglish} in queue | ${timeRemaining} time remaining`); - - return embed; - }; - - var myMessage = null; - - function displayPage(number) { - let page = generatePage(lists[number - 1], number); - - if(page) { - if(myMessage) { - myMessage.edit(page); + if (aaa.length > 1024) { + return [list, start + i - 1] } else { - myMessage = message.channel.send(page); + list = aaa } - return true; - } else { - return false; + // totalDuration = totalDuration + song.duration } - }; - function aFunction(start) { + return [list, start + i + 1] + } + + const songsInQueue = queue.length - 1 + let songsInQueueEnglish = 'song' + + function generatePage (list, page) { + if (!list || list === '') { + return false + } + + var embed = new Discord.MessageEmbed() + embed.setTitle(`Queue for: ${message.guild.name}`) + embed.setColor(client.embedColour(message)) + + var elapsedTime = getGuild(message.guild.id).dispatcher.streamTime / 1000 + var totalDuration = queue[0].video.lengthSeconds - elapsedTime + + let timeRemaining = '' + + for (let i = 1; i < queue.length; i++) { + const b = queue[i] + + if (b.video.lengthSeconds === 0) { + timeRemaining = 'āˆž' + + break + } + + totalDuration += b.video.lengthSeconds + } + + if (timeRemaining === '') { + const queueDuration = createTimestamp(totalDuration) + + timeRemaining = queueDuration + } + + let timestamp = `\`${createTimestamp(queue[0].video.lengthSeconds)}\`` + + if (timestamp !== '`[LIVE]`') { + timestamp = `\`[${createTimestamp(elapsedTime) + '/' + createTimestamp(queue[0].video.lengthSeconds)}]\`` + } + + embed.addField('Now playing:', `**[${queue[0].video.title}](https://www.youtube.com/watch?v=${queue[0].video.videoId})** added by ${queue[0].requestedBy} ${timestamp}`) + + embed.addField('Up next:', list) + + if (songsInQueue > 1 || songsInQueue === 0) { + songsInQueueEnglish = 'songs' + } + + embed.setFooter(`Page ${page}/${lists.length} | ${songsInQueue + ' ' + songsInQueueEnglish} in queue | ${timeRemaining} time remaining`) + + return embed + } + + var myMessage = null + + function displayPage (number) { + const page = generatePage(lists[number - 1], number) + + if (page) { + if (myMessage) { + myMessage.edit(page) + } else { + myMessage = message.channel.send(page) + } + + return true + } else { + return false + } + } + + function aFunction (start) { // start - index of song, which we should start with // end - index of song, which we ended with - let [list, end] = generateList(start, lists.length + 1); + const [list, end] = generateList(start, lists.length + 1) - if(list && list != "") { - lists.push(list); - - if(queue[end + 1]) { - aFunction(end + 1); + if (list && list !== '') { + lists.push(list) + + if (queue[end + 1]) { + aFunction(end + 1) } } - }; + } - aFunction(1); + aFunction(1) - let page = 1; + let page = 1 - if(args[0]) { - let userPage = Number(args[0]); + if (args[0]) { + const userPage = Number(args[0]) - if(userPage) { - page = userPage; + if (userPage) { + page = userPage } else { return message.channel.send( - `<:error:466995152976871434> Invalid page. Usage: \`${client.commands.get(`queue`).help.usage}\`` - ); + `<:error:466995152976871434> Invalid page number. Usage: \`${client.commands.get('queue').help.usage}\`` + ) } - }; + } - if(displayPage(page)) { + if (displayPage(page)) { } else { return message.channel.send( `<:error:466995152976871434> Page ${page} doesn't exist!` - ); + ) } -}; +} exports.conf = { enabled: true, diff --git a/src/commands/rate.js b/src/commands/rate.js index d4f0eac..2a23266 100644 --- a/src/commands/rate.js +++ b/src/commands/rate.js @@ -3,19 +3,25 @@ exports.run = async (client, message, args) => { return message.channel.send( `<:error:466995152976871434> What am I meant to rate? Usage: \`${client.commands.get(`rate`).help.usage}\`` ); -var rating = [ - "0/10", - "1/10", - "2/10", - "3/10", - "4/10", - "5/10", - "6/10", - "7/10", - "8/10", - "9/10", - "10/10" -]; + + var rating = [ + "0/10", + "1/10", + "2/10", + "3/10", + "4/10", + "5/10", + "6/10", + "7/10", + "8/10", + "9/10", + "10/10" + ]; + + if (message.content.includes("@everyone") || message.content.includes("@here") || message.content.includes("<@&")) { + return message.channel.send('>:('); + }; + let mess = rating.random(); message.channel.send(`<:star:618393201501536258> I give ${args.join(" ")} a **${mess}**`); }; diff --git a/src/commands/removesong.js b/src/commands/removesong.js index d576c7d..357720f 100644 --- a/src/commands/removesong.js +++ b/src/commands/removesong.js @@ -1,36 +1,30 @@ -const util = require("util") -const Discord = require("discord.js") - +const { getGuild } = require('../modules/music') module.exports.run = (client, message, args, level) =>{ - var queue = client.music.getGuild(message.guild.id).queue; + var queue = getGuild(message.guild.id).queue - if(queue.length < 2) { - return message.channel.send(`<:error:466995152976871434> Not enough songs are in the queue for this command to work!`); + if (queue.length < 2) { + return message.channel.send('<:error:466995152976871434> Not enough songs are in the queue for this command to work!') } - if(!args[0]) { - return message.channel.send(`<:error:466995152976871434> You didn't tell me what song to remove! Usage: \`${client.commands.get(`removesong`).help.usage}\``); - }; + if (!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't tell me what song to remove! Usage: \`${client.commands.get('removesong').help.usage}\``) + } - var input = +args[0]; + var input = +args[0] - if(isNaN(input) == true) { - return message.channel.send(`<:error:466995152976871434> That isn't a number! You need to tell me the songs position in the queue (1, 2, etc.)`); - }; + if (isNaN(input) === true) { + return message.channel.send('<:error:466995152976871434> That isn\'t a number! You need to tell me the songs position in the queue (1, 2, etc.)') + } - if(input >= queue.length) { - return message.channel.send("Invalid (too large)"); - }; + if (input >= queue.length || input < 1) { + return message.channel.send('<:error:466995152976871434> Input is not a valid song ID.') + } - if(input < 1) { - return message.channel.send("Invalid (too small)"); - }; + var songName = queue[input].video.title - var songName = queue[input].title; + queue.splice(input, 1) - queue.splice(input, 1); - - message.channel.send(`<:success:466995111885144095> Removed from queue: **${songName}**`); + message.channel.send(`<:success:466995111885144095> Removed from queue: **${songName}**`) }; exports.conf = { @@ -38,7 +32,7 @@ exports.conf = { guildOnly: true, aliases: ["rmsong"], permLevel: "Moderator", - requiredPerms: ["SPEAK"] + requiredPerms: [] }; exports.help = { diff --git a/src/commands/restart.js b/src/commands/restart.js index f3cf1a7..dfd005a 100644 --- a/src/commands/restart.js +++ b/src/commands/restart.js @@ -1,14 +1,21 @@ -exports.run = async (client, message) => {// eslint-disable-line no-unused-vars +const fetch = require('node-fetch'); + +exports.run = (client, message) => {// eslint-disable-line no-unused-vars // This actually shuts down the bot, you'll need to use something like pm2 to get it to restart - await message.channel.send("<:reboot:467216876938985482> Restarting..."); + message.channel.send("<:reboot:467216876938985482> Restarting..."); + + client.destroy(); + require("util").promisify(setTimeout); - client.commands.forEach( async cmd => { - await client.unloadCommand(cmd); + fetch('https://gamecp.apex.to/api/client/servers/1fc76afa-9a4d-497b-983a-a898795ab5b5/power', { + method: 'post', + body: JSON.stringify({ 'signal': 'restart' }), + headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${client.config.server}` } + }).catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); }); - - process.exit(); }; exports.conf = { diff --git a/src/commands/resume.js b/src/commands/resume.js index 9c63f87..051cdfc 100644 --- a/src/commands/resume.js +++ b/src/commands/resume.js @@ -1,15 +1,20 @@ -const Discord = require("discord.js") +const { getGuild } = require('../modules/music') exports.run = (client, message, args, level) => { - let guild = client.music.getGuild(message.guild.id); - if(guild.queue.length < 1) { - return message.channel.send("<:error:466995152976871434> Nothing is playing."); - }; - guild.playing = true; - guild.paused = false; - guild.dispatcher.resume(); - message.channel.send("<:play:467216788187512832> Playback resumed!"); + const guild = getGuild(message.guild.id) + if (guild.paused === false) { + return message.channel.send('<:error:466995152976871434> The music is already playing, use pause to pause the music first!') + } + if (guild.queue.length < 1) { + return message.channel.send('<:error:466995152976871434> Nothing is playing!') + } + + guild.playing = true + guild.paused = false + guild.dispatcher.resume() + + message.channel.send('<:success:466995111885144095> Music playback has been resumed.') }; exports.conf = { diff --git a/src/commands/rip.js b/src/commands/rip.js index d677655..5039140 100644 --- a/src/commands/rip.js +++ b/src/commands/rip.js @@ -1,12 +1,18 @@ -var request = require('request'); +const fetch = require('node-fetch'); const Discord = require("discord.js") exports.run = (client, message) => { message.channel.startTyping(); - var r = request.get('http://mityurl.com/y/yKsQ/r', function (err, res, body) { - var rip = r.uri.href - message.channel.send(`>:] ${rip}`) - message.channel.stopTyping(); - }); + try{ + fetch('http://mityurl.com/y/yKsQ/r', { redirect: 'follow' }) + .then(res => res) + .then(res => message.channel.send(`>:] ${res.url}`)) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); + } catch(err) { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }; + message.channel.stopTyping(); } exports.conf = { diff --git a/src/commands/salmonrun.js b/src/commands/salmonrun.js new file mode 100644 index 0000000..737b158 --- /dev/null +++ b/src/commands/salmonrun.js @@ -0,0 +1,77 @@ +const Discord = require("discord.js"); +const BasePaginator = require('discord-paginator.js'); +const fetch = require('node-fetch'); +const prettifyMiliseconds = require('pretty-ms'); + + +exports.run = async (client, message, args) =>{ + fetch('https://splatoon2.ink/data/coop-schedules.json', { headers: { 'User-Agent': client.config.userAgent }}) + .then(res => res.json()) + .then(json => { + fetch('https://splatoon2.ink/data/timeline.json', { headers: { 'User-Agent': client.config.userAgent }}) + .then(timelineRes => timelineRes.json()) + .then(timelineJson => { + + const embeds = []; + + if ((json.details[0].start_time * 1000) > Date.now() === true) { + embeds.push( + new Discord.MessageEmbed() + .setTitle('Upcoming Salmon Run') + .setColor(client.embedColour(message)) + .setImage('https://splatoon2.ink/assets/splatnet/'+json.details[0].stage.image) + .addField('Map', json.details[0].stage.name, true) + .setFooter(`Page 1/2 | Starting in ${prettifyMiliseconds(json.details[0].start_time * 1000 - Date.now(), { secondsDecimalDigits: 0 })} | Data provided by splatoon2.ink`) + ); + } else { + embeds.push( + new Discord.MessageEmbed() + .setTitle('Current Salmon Run') + .setColor(client.embedColour(message)) + .setThumbnail('https://splatoon2.ink/assets/splatnet'+timelineJson.coop.reward_gear.gear.image) + .setImage('https://splatoon2.ink/assets/splatnet/'+json.details[0].stage.image) + .addField('Map', json.details[0].stage.name, true) + .addField('Reward Gear', timelineJson.coop.reward_gear.gear.name, true) + .addField('Weapons', json.details[0].weapons[0].weapon.name+', '+json.details[0].weapons[1].weapon.name+', '+json.details[0].weapons[2].weapon.name+', '+json.details[0].weapons[3].weapon.name) + .setFooter(`Page 1/2 | Ending in ${prettifyMiliseconds((json.details[0].end_time * 1000) - Date.now(), { secondsDecimalDigits: 0 })} | Data provided by splatoon2.ink`) + ); + } + + embeds.push( + new Discord.MessageEmbed() + .setTitle('Upcoming Salmon Run') + .setColor(client.embedColour(message)) + .setImage('https://splatoon2.ink/assets/splatnet/'+json.details[1].stage.image) + .addField('Map', json.details[1].stage.name, true) + .addField('Weapons', json.details[1].weapons[1].weapon.name+', '+json.details[1].weapons[1].weapon.name+', '+json.details[1].weapons[2].weapon.name+', '+json.details[1].weapons[3].weapon.name) + .setFooter(`Page 2/2 | Starting in ${prettifyMiliseconds(json.details[1].start_time * 1000 - Date.now(), { secondsDecimalDigits: 0 })} | Data provided by splatoon2.ink`) + ); + + const Paginator = new BasePaginator({ + pages: embeds, + timeout: 120000, + filter: (reaction, user) => user.id == message.author.id //to filter the reaction collector + }) + + Paginator.spawn(message.channel) + }); + }) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: [], + permLevel: "User", + requiredPerms: [] + }; + +exports.help = { + name: "salmonrun", + category: "Splatoon", + description: "Get current map, weapons and gear for salmon run.", + usage: "salmonrun" + }; diff --git a/src/commands/say.js b/src/commands/say.js index e196847..917e1fc 100644 --- a/src/commands/say.js +++ b/src/commands/say.js @@ -4,8 +4,8 @@ exports.run = (client, message, args, level) => { `<:error:466995152976871434> No message provided. Usage: \`${client.commands.get(`echo`).help.usage}\`` ); }; - if (message.content.includes("@everyone")) { - return message.channel.send(`<@${message.author.id}>`); + if (message.content.includes("@everyone") || message.content.includes("@here") || message.content.includes("<@&")) { + return message.channel.send('>:('); }; message.delete().catch(O_o => {}); @@ -24,5 +24,5 @@ exports.help = { name: "say", category: "Fun", description: "Makes Woomy copy what the user says.", - usage: "echo <-hide> [message]" + usage: "echo [message]" }; diff --git a/src/commands/settings.js b/src/commands/settings.js index eb1f66c..6bdd2b4 100644 --- a/src/commands/settings.js +++ b/src/commands/settings.js @@ -80,7 +80,7 @@ exports.run = async (client, message, args) => { embed.setAuthor("Settings for: " + message.guild.name, message.guild.iconURL({dynamic: true})) embed.setColor(message.guild.member(client.user).displayHexColor) embed.setDescription("You can edit these settings using the commands in the 'configure' section of the help command.") - embed.addFields({ name: "General:", value: `Prefix: \`${prefix}\`\nChat logging: ${chatChan}\nMod logging: ${modChan}\nRaid mode: ${raidMode}\nJoin/leave channel: ${greetChan}\nWelcome message: ${welcomeMessage}\nLeave message: ${leaveMessage}`, inline: true}, {name: "Roles:", value: `Moderator: ${modRole}\nAdministrator: ${adminRole}\nMuted: ${mutedRole}\nBlacklisted: ${blacklist}\nAutorole: ${autorole}`, inline: true}) + embed.addFields({ name: "General:", value: `Prefix: \`${prefix}\`\nChat logging: ${chatChan}\nMod logging: ${modChan}\nRaid mode: ${raidMode}\nJoin/leave channel: ${greetChan}\nWelcome message: ${welcomeMessage}\nLeave message: ${leaveMessage}`, inline: true}, {name: "Roles:", value: `Moderator: ${modRole}\nAdministrator: ${adminRole}\nMuted: ${mutedRole}\nBlocklist: ${blacklist}\nAutorole: ${autorole}`, inline: true}) message.channel.send(embed) }; diff --git a/src/commands/ship.js b/src/commands/ship.js index 3850d87..2b21d0d 100644 --- a/src/commands/ship.js +++ b/src/commands/ship.js @@ -1,40 +1,58 @@ +const Discord = require('discord.js') exports.run = async (client, message, args) => { - - var name, name1; - var rating = Math.floor(Math.random() * 100) + 1; + var rating = Math.floor(Math.random() * 100) + 1 + var meter = ['ā–¬', 'ā–¬', 'ā–¬', 'ā–¬', 'ā–¬', 'ā–¬', 'ā–¬', 'ā–¬', 'ā–¬'] var hearts = [ - "ā¤ļø", - "🧔", - "šŸ’›", - "šŸ’š", - "šŸ’™", - "šŸ’œ" - ]; - - if(args.length < 2) { - return message.channel.send(`<:error:466995152976871434> Please include two names/users.`) + 'ā¤ļø', + '🧔', + 'šŸ’›', + 'šŸ’š', + 'šŸ’™', + 'šŸ’œ' + ] + + if (!args[0]) { + return message.channel.send( + `<:error:466995152976871434> No message provided. Usage: \`${client.commands.get(`ship`).help.usage}\`` + ); } - if(message.guild && message.mentions.members && message.mentions.members.size > 0) { - name = message.mentions.members.first().displayName; - }; + if (!args[1]) { + return message.channel.send( + `<:error:466995152976871434> No message provided. Usage: \`${client.commands.get(`ship`).help.usage}\`` + ); + } - if(message.guild && message.mentions.members && message.mentions.members.size > 1) { - name1 = message.mentions.members.last().displayName; - }; + const firstName = args[0] + const secondName = args[1] - if(!name) { - name = args[0]; - }; + const shipName = firstName.substr(0, firstName.length * 0.5) + secondName.substr(secondName.length * 0.5) - if(!name1) { - name1 = args[1]; - }; + if (shipName.toLowerCase() === 'teily' || shipName.toLowerCase() === 'emrra') { + rating = '100' + } - shipName = name.substr(0, client.intBetween(1,name.length))+name1.substr(client.intBetween(0,name1.length)); + var pos = 0 + var under = 9 + while (pos < 10) { + if (rating < under) { + meter.splice(pos, 0, hearts.random()) + break + } + pos++ + under += 10 + } - message.channel.send(`__**Ship Generator:**__\n${hearts.random()} Ship Name: \`${shipName}\`\n${hearts.random()} Compatibility rating: \`${rating}%\``) -}; + if (rating >= 99) { + meter.splice(9, 0, hearts.random()) + } + + const embed = new Discord.MessageEmbed() + embed.setTitle(`Original Names: ${firstName}, ${secondName}`) + embed.setColor(client.embedColour(message.guild)) + embed.setDescription(`Ship Name: **${shipName}**\nCompatibility: **${rating}%**\n**[**${meter.join('')}**]**`) + message.channel.send(embed) +} exports.conf = { enabled: true, @@ -48,6 +66,6 @@ exports.help = { name: "ship", category: "Fun", description: "Ship two people together <3", - usage: "ship [name/user] [name/user]" + usage: "ship [name1] [name2]" }; diff --git a/src/commands/shuffle.js b/src/commands/shuffle.js new file mode 100644 index 0000000..23270e5 --- /dev/null +++ b/src/commands/shuffle.js @@ -0,0 +1,34 @@ +const { getGuild } = require('../modules/music') +exports.run = async (client, message) => { + var queue = getGuild(message.guild.id).queue + + if (queue.length < 4) { + return message.channel.send('<:error:466995152976871434> There aren\'t enough songs are in the queue for this command to work!') + } + + const max = queue.length - 1 + const min = 1 + for (let i = max; i >= min; i--) { + const randomIndex = Math.floor(Math.random() * (max - min + 1)) + min + const itemAtIndex = queue[randomIndex] + queue[randomIndex] = queue[i] + queue[i] = itemAtIndex + } + + message.channel.send('<:success:466995111885144095> Queue shuffled!') +} + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "Moderator", + requiredPerms: [] +} + +exports.help = { + name: 'shuffle', + category: 'Music', + description: 'Mixes up the songs in the queue', + usage: 'shuffle' +} \ No newline at end of file diff --git a/src/commands/skip.js b/src/commands/skip.js index c997c18..c9b83e0 100644 --- a/src/commands/skip.js +++ b/src/commands/skip.js @@ -1,49 +1,54 @@ -const Discord = require("discord.js") +const { skip, getGuild } = require('../modules/music') exports.run = (client, message, args, level) => { - let guild = client.music.getGuild(message.guild.id); + const guild = getGuild(message.guild.id) - if(guild.queue.length < 1 || !guild.playing || !guild.dispatcher) return message.channel.send( - "<:error:466995152976871434> Nothing is playing." - ); - - let vc = message.guild.members.cache.get(client.user.id).voiceChannel; - - if(vc != message.member.voiceChannel) return message.channel.send( - '<:error:466995152976871434> You need to be in my voice channel to use this command!' - ); - - if(guild.queue[0].requestedBy.id == message.author.id) { - skip_song(guild); - - message.channel.send( - `<:skip:467216735356059660> Song has been skipped by the user who requested it.` - ); - - return; + if (guild.queue.length < 1 || !guild.playing || !guild.dispatcher) { + return message.channel.send( + '<:error:466995152976871434> Nothing is playing.' + ) } - if (guild.skippers.indexOf(message.author.id) == -1) { - guild.skippers.push(message.author.id); + const vc = message.guild.members.cache.get(client.user.id).voice.channel + + if (vc !== message.member.voice.channel) { + return message.channel.send( + '<:error:466995152976871434> You need to be in my voice channel to use this command!' + ) + } + + if (guild.queue[0].requestedBy.id === message.author.id) { + skip(message.guild, 'skip') + + guild.skippers = [] + + message.channel.send( + '<:success:466995111885144095> Song has been skipped by the user who requested it.' + ) + + return + } + + if (guild.skippers.indexOf(message.author.id) === -1) { + guild.skippers.push(message.author.id) if (guild.skippers.length >= Math.ceil(vc.members.filter(member => !member.user.bot).size / 2)) { - - skip_song(guild); + skip(message.guild, 'skip') + + guild.skippers = [] message.channel.send( - `<:skip:467216735356059660> Song has been skipped.` - ); - + '<:skip:467216735356059660> Song skipped.' + ) } else { message.channel.send( - `<:success:466995111885144095> Your vote has been acknowledged! **${guild.skippers.length + "/" + Math.ceil(vc.members.filter(member => !member.user.bot).size / 2)}**` - ); + `<:success:466995111885144095> Your vote has been acknowledged! **${guild.skippers.length + '/' + Math.ceil(vc.members.filter(member => !member.user.bot).size / 2)}**` + ) }; - } else { message.channel.send( - "<:denied:466995195150336020> You cannot vote twice!" - ); - }; + '<:denied:466995195150336020> You cannot vote twice!' + ) + } }; exports.conf = { @@ -51,7 +56,7 @@ exports.conf = { guildOnly: true, aliases: ["voteskip"], permLevel: "User", - requiredPerms: ["SPEAK"] + requiredPerms: [] }; exports.help = { diff --git a/src/commands/songinfo.js b/src/commands/songinfo.js new file mode 100644 index 0000000..ac74bbb --- /dev/null +++ b/src/commands/songinfo.js @@ -0,0 +1,46 @@ +const { getGuild, createTimestamp } = require('../modules/music') +const { MessageEmbed } = require('discord.js') +exports.run = async (client, message, args) => { + const guild = getGuild(message.guild.id) + + if (guild.queue.length < 1) { + return message.channel.send(client.config.emojis.error + ' Nothing is in the queue!') + } + + const songID = +args[0] + + if (isNaN(songID) === true) { + return message.channel.send('<:error:466995152976871434> That isn\'t a number! You need to tell me the songs position in the queue (1, 2, etc.)') + } + + const s = guild.queue[songID] + + if (!s) { + return message.channel.send('<:error:466995152976871434> No song was found in the position you specified.') + } + + const embed = new MessageEmbed() + embed.setThumbnail(client.config.endpoints.invidious + s.video.videoThumbnails[1].url) + embed.setColor(client.embedColour(message)) + embed.setDescription(`**[${s.video.title}](https://www.youtube.com/watch?v=${s.video.videoId})**`) + embed.addField('Channel:', s.video.author, true) + embed.addField('Length:', '`[' + createTimestamp(s.video.lengthSeconds) + ']`', true) + embed.setFooter('Requested by ' + s.requestedBy.tag, s.requestedBy.avatarURL({ format: 'png', dynamic: true, size: 2048 })) + + message.channel.send(embed) +} + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: [] +} + +exports.help = { + name: "songinfo", + category: "Music", + description: "Sends you information about a song in the queue. Song ID is the song's position in the queue.", + usage: "songinfo [songID]" +} \ No newline at end of file diff --git a/src/commands/splatnet.js b/src/commands/splatnet.js new file mode 100644 index 0000000..50d3c0c --- /dev/null +++ b/src/commands/splatnet.js @@ -0,0 +1,53 @@ +const Discord = require("discord.js"); +const BasePaginator = require('discord-paginator.js'); +const fetch = require('node-fetch'); +const prettifyMiliseconds = require('pretty-ms'); + + +exports.run = async (client, message, args) =>{ + fetch('https://splatoon2.ink//data/merchandises.json', { headers: { 'User-Agent': client.config.userAgent }}) + .then(res => res.json()) + .then(json => { + const embeds = []; + + for ( let i = 0; i < json.merchandises.length; i++ ) { + const embed = new Discord.MessageEmbed() + .setTitle(json.merchandises[i].gear.name) + .setThumbnail('https://splatoon2.ink/assets/splatnet' + json.merchandises[i].gear.image) + .setColor(client.embedColour(message)) + .addField('Price', (json.merchandises[i].price).toString(), true) + .addField('Brand', json.merchandises[i].gear.brand.name, true) + .addField('Ability Slots', (json.merchandises[i].gear.rarity + 1).toString(), true) + .addField('Main Ability', json.merchandises[i].skill.name, true) + .addField('Common Ability', json.merchandises[i].gear.brand.frequent_skill.name, true) + .setFooter(`Page ${i+1}/${json.merchandises.length} | Out of stock in ${prettifyMiliseconds(json.merchandises[i].end_time * 1000 - Date.now())} | Data provided by splatoon2.ink`); + embeds.push(embed); + } + + const Paginator = new BasePaginator({ + pages: embeds, + timeout: 120000, + filter: (reaction, user) => user.id == message.author.id //to filter the reaction collector + }) + + Paginator.spawn(message.channel) + }) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: [], + permLevel: "User", + requiredPerms: [] + }; + +exports.help = { + name: "splatnet", + category: "Splatoon", + description: "See what is currently on offer in the splatnet shop.", + usage: "splatnet" + }; diff --git a/src/commands/splatoonmaps.js b/src/commands/splatoonmaps.js new file mode 100644 index 0000000..22941c1 --- /dev/null +++ b/src/commands/splatoonmaps.js @@ -0,0 +1,60 @@ +const Discord = require("discord.js"); +const BasePaginator = require('discord-paginator.js'); +const fetch = require('node-fetch'); +const prettifyMiliseconds = require('pretty-ms'); + + +exports.run = async (client, message, args) =>{ + fetch('https://splatoon2.ink/data/schedules.json', { headers: { 'User-Agent': client.config.userAgent }}) + .then(res => res.json()) + .then(json => { + + const embeds = [ + new Discord.MessageEmbed() + .setTitle('Current Splatoon 2 Maps') + .setColor(client.embedColour(message)) + .addField('<:turf_war:814651383911153692> Turf War', `${json.regular[0].stage_a.name}\n${json.regular[0].stage_b.name}`, true) + .addField(`<:ranked:814651402479468544> Ranked: ${json.gachi[0].rule.name}`, `${json.gachi[0].stage_a.name}\n${json.gachi[0].stage_b.name}`, true) + .addField(`<:league:814651415409590363> League: ${json.league[0].rule.name}`, `${json.league[0].stage_a.name}\n${json.league[0].stage_b.name}`, true) + .setFooter(`Page 1/${json.regular.length} | Maps changing in ${prettifyMiliseconds(json.league[0].end_time * 1000 - Date.now(), { secondsDecimalDigits: 0 })} | Data provided by splatoon2.ink`) + ]; + + for ( let i = 1; i < json.regular.length; i++ ) { + embeds.push( + new Discord.MessageEmbed() + .setTitle('Upcoming Splatoon 2 Maps') + .setColor(client.embedColour(message)) + .addField('<:turf_war:814651383911153692> Turf War', `${json.regular[i].stage_a.name}\n${json.regular[i].stage_b.name}`, true) + .addField(`<:ranked:814651402479468544> Ranked: ${json.gachi[i].rule.name}`, `${json.gachi[i].stage_a.name}\n${json.gachi[i].stage_b.name}`, true) + .addField(`<:league:814651415409590363> League: ${json.league[i].rule.name}`, `${json.league[i].stage_a.name}\n${json.league[i].stage_b.name}`, true) + .setFooter(`Page ${i+1}/${json.regular.length} | Available in ${prettifyMiliseconds(json.league[i].start_time * 1000 - Date.now(), { secondsDecimalDigits: 0 })} | Data provided by splatoon2.ink`) + ); + } + + const Paginator = new BasePaginator({ + pages: embeds, + timeout: 120000, + filter: (reaction, user) => user.id == message.author.id //to filter the reaction collector + }) + + Paginator.spawn(message.channel) + }) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ['splatoonmodes'], + permLevel: "User", + requiredPerms: [] + }; + +exports.help = { + name: "splatoonmaps", + category: "Splatoon", + description: "Get current and upcoming maps and modes for regular, ranked and league battles.", + usage: "splatoonmaps" + }; diff --git a/src/commands/stop.js b/src/commands/stop.js index 695dd04..4f5a3df 100644 --- a/src/commands/stop.js +++ b/src/commands/stop.js @@ -1,18 +1,20 @@ -const Discord = require("discord.js"); - +const { getGuild } = require('../modules/music') exports.run = async (client, message) => { - let guild = client.music.getGuild(message.guild.id); + const guild = getGuild(message.guild.id) - if(guild.queue.length < 1 || !guild.playing || !guild.dispatcher) return message.channel.send("<:error:466995152976871434> Nothing is playing."); - if(!message.member.voice.channel) return message.channel.send('<:error:466995152976871434> You need to be in voice channel to use this command!'); + if (guild.queue.length < 1 || !guild.playing || !guild.dispatcher) return message.channel.send('Nothing is playing.') + if (!message.member.voice.channel) return message.channel.send('You need to be in voice channel to use this command!') - guild.playing = false; - guild.paused = false; - guild.queue = []; + guild.dispatcher.end('silent') - guild.dispatcher.end("silent"); + guild.queue = [] + guild.playing = false + guild.paused = false + guild.skippers = [] + guild.fixers = [] + guild.channel = null - message.channel.send("<:stop:467639381390262284> Playback stopped!"); + message.channel.send('<:success:466995111885144095> Playback stopped!') }; exports.conf = { diff --git a/src/commands/userinfo.js b/src/commands/userinfo.js index 98d6738..9a42f4f 100644 --- a/src/commands/userinfo.js +++ b/src/commands/userinfo.js @@ -1,8 +1,8 @@ const Discord = require("discord.js"); const coolPeople = require('../../resources/other/coolpeople.json') exports.run = (client, message, args) => { - var user, guild, status, createdAt, avurl, tag, id; - var nick = "", roles = "", presence = "", badges = ""; + var user, guild, createdAt, avurl, tag, id; + var nick = "", roles = "", badges = ""; var coolPerson = false; var friendos = coolPeople.coolPeople; @@ -74,41 +74,10 @@ exports.run = (client, message, args) => { createdAt = user.createdAt; }; - if(user.presence.status == "online") { - status = `online <:status_online:685462758023626762>` - }; - - if(user.presence.status == "idle") { - status = `idle <:status_idle:685462771529154561>` - }; - - if(user.presence.status == "dnd") { - status = `do not disturb <:status_dnd:685462782963220495>` - }; - - if(user.presence.status == "offline") { - status = `offline <:status_offline:685462758229016633>` - }; - - if(user.presence.activities[0]) { - presence = "\n• **Presence:** "; - if(user.presence.activities[0].type == "PLAYING") { - presence += `Playing ${user.presence.activities[0].name}`; - }; - - if(user.presence.activities[0].type == "STREAMING") { - presence += `Streaming ${user.presence.activities[0].name}`; - }; - - if(user.presence.activities[0].type == "CUSTOM_STATUS") { - presence += `${user.presence.activities[0].state}`; - }; - }; - embed = new Discord.MessageEmbed(); embed.setTitle(tag); embed.setThumbnail(avurl); - embed.setDescription(`${badges}• **ID:** ${id}${nick}\n• **Status:** ${status}${presence}${guild}\n• **Account created:** ${createdAt}`) + embed.setDescription(`${badges}• **ID:** ${id}${nick}${guild}\n• **Account created:** ${createdAt}`) embed.setColor(colour); message.channel.send(embed); }; diff --git a/src/commands/volume.js b/src/commands/volume.js new file mode 100644 index 0000000..6711947 --- /dev/null +++ b/src/commands/volume.js @@ -0,0 +1,57 @@ +const { getGuild, setVolume } = require('../modules/music') +exports.run = async (client, message, args) => { + if (!args[0]) { + return message.channel.send(`<:error:466995152976871434> No input! Usage: \`${client.commands.get('volume').help.usage}\``) + } + + const guild = getGuild(message.guild.id) + + if (guild.queue.length < 1 || !guild.playing || !guild.dispatcher) { + return message.channel.send( + '<:error:466995152976871434> Nothing is playing.' + ) + } + + let userVolume = args[0] + + if (userVolume.includes('%')) { + userVolume = userVolume.replace('%', '') + } + + userVolume = +userVolume + + if (isNaN(userVolume) === true) { + return message.channel.send('<:error:466995152976871434> Input must be a number!') + } + + if (userVolume > 100 || userVolume < 1) { + return message.channel.send('<:error:466995152976871434> Invalid input, input must be between 1-100') + } + + if (userVolume) { + userVolume = Number(userVolume) + + userVolume = userVolume / 100 + + if (userVolume <= 1) { + setVolume(message.guild, userVolume) + + message.channel.send('<:success:466995111885144095> Set volume to ' + userVolume * 100 + '%') + } + } +} + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "Moderator", + requiredPerms: [] +} + +exports.help = { + name: 'volume', + category: 'Music', + description: 'Sets volume of currently playing music. (100% = 25% of the actual volume)', + usage: 'volume [volume]' +} \ No newline at end of file diff --git a/src/commands/wag.js b/src/commands/wag.js new file mode 100644 index 0000000..a3a5a04 --- /dev/null +++ b/src/commands/wag.js @@ -0,0 +1,31 @@ +const fetch = require("node-fetch") +exports.run = async (client, message, args) => { + message.channel.startTyping(); + try{ + fetch(`https://purrbot.site/api/img/sfw/tail/gif/`) + .then(res => res.json()) + .then(json => message.channel.send(json.link)) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); + message.channel.stopTyping(); + } catch(err) { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "wag", + category: "Image", + description: "Wag the tail :3", + usage: "wag" +}; diff --git a/src/commands/weather.js b/src/commands/weather.js index ef01bc8..880436c 100644 --- a/src/commands/weather.js +++ b/src/commands/weather.js @@ -44,6 +44,7 @@ exports.run = async (client, message, args, error) => { message.channel.send(embed) }); } catch(err) { + message.channel.stopTyping(); // Previously wasnt here causing an issue where woomy would endlessly type. return message.channel.send(`<:error:466995152976871434> API error: \`${err}\``) }; }; diff --git a/src/commands/yoda.js b/src/commands/yoda.js index 496b929..9b71065 100644 --- a/src/commands/yoda.js +++ b/src/commands/yoda.js @@ -9,7 +9,10 @@ exports.run = async (client, message, args) => { try{ fetch(`http://yoda-api.appspot.com/api/v1/yodish?text=${encodeURIComponent(speech.toLowerCase())}`) .then(res => res.json()) - .then(json => message.channel.send(json.yodish)); + .then(json => message.channel.send(json.yodish)) + .catch(err => { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }); message.channel.stopTyping(); } catch(err) { message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); diff --git a/src/events/message.js b/src/events/message.js index 7c399a8..845e76e 100644 --- a/src/events/message.js +++ b/src/events/message.js @@ -1,7 +1,8 @@ const cooldown = new Set(); module.exports = async (client, message) => { if (message.author.bot) return; - + if (typeof(message.content) === 'string') message.content = message.content.replace(/\u8203/g,'').replace(/\uB200/g,'').replace("\\uB200",''); // Remove zero-width characters + var settings; if(message.guild) { @@ -163,10 +164,14 @@ module.exports = async (client, message) => { return message.author.send(`<:error:466995152976871434> I don't have permission to speak in **#${message.channel.name}**, Please ask a moderator to give me the send messages permission!`); }; + if (!cmd.conf.enabled) { + return message.channel.send('<:error:466995152976871434> This command has been disabled by my developers.') + } + if(message.guild && blacklisted == true) { try { return message.author.send( - `<:denied:466995195150336020> You have been blacklisted from using commands in \`${message.guild.name}\`` + `<:denied:466995195150336020> You have been blocked from using commands in \`${message.guild.name}\`` ); } catch(err) { client.logger.log(err, "error") @@ -229,7 +234,7 @@ module.exports = async (client, message) => { cooldown.delete(message.author.id); }, 2000); - client.logger.cmd(`${client.config.permLevels.find(l => l.level === level).name} ${message.author.username} (${message.author.id}) ran command ${cmd.help.name}`); + client.logger.cmd(`${client.config.permLevels.find(l => l.level === level).name} ran command ${cmd.help.name}`); cmd.run(client, message, args, level); }; diff --git a/src/events/messageDelete.js b/src/events/messageDelete.js index c697f3e..97a424a 100644 --- a/src/events/messageDelete.js +++ b/src/events/messageDelete.js @@ -3,6 +3,8 @@ const Discord = require("discord.js"); module.exports = (client, message) => { if (message.author.bot) return; + if(!message.guild) return; + const settings = (message.settings = client.getSettings(message.guild.id)); if (settings.chatlogsChannel !== "off") { diff --git a/src/events/messageUpdate.js b/src/events/messageUpdate.js index c6aa6e8..c7623e1 100644 --- a/src/events/messageUpdate.js +++ b/src/events/messageUpdate.js @@ -13,6 +13,7 @@ module.exports = (client, omsg, nmsg) => { ); if (channel) { + if (!nmsg.member) return; let embed = new Discord.MessageEmbed(); embed.setColor("#fff937"); embed.setAuthor("Message Edited!", nmsg.member.user.avatarURL({dynamic: true})); @@ -29,7 +30,7 @@ module.exports = (client, omsg, nmsg) => { return; } - embed.setDescription(`• Author: ${nmsg.member} (${nmsg.member.user.id})\n• Channel: ${nmsg.channel}\n• Old message: ${omsg.content}\n• New message: ${nmsg.content}`) + embed.setDescription(`[Jump to message](https://discord.com/channels/${nmsg.guild.id}/${nmsg.channel.id}/${nmsg.id})\n• Author: ${nmsg.member} (${nmsg.member.user.id})\n• Channel: ${nmsg.channel}\n• Old message: ${omsg.content}\n• New message: ${nmsg.content}`) try { channel.send({ embed }); } catch (err) { diff --git a/src/events/voiceStateUpdate.js b/src/events/voiceStateUpdate.js new file mode 100644 index 0000000..34fd833 --- /dev/null +++ b/src/events/voiceStateUpdate.js @@ -0,0 +1,42 @@ +// Copyright 2020 Emily J. / mudkipscience and contributors. Subject to the AGPLv3 license. + +const music = require('../modules/music') + +module.exports = (client, oldState, newState) => { + if (newState.channelID !== oldState.channelID) { + const guild = music.getGuild(newState.guild.id) + + // Reset queue, dispatcher, etc if Woomy is forcibly disconnected from the queue + if (guild.voiceChannel && !guild.voiceChannel.members.get(client.user.id) && guild.queue.length > 0) { + guild.queue = [] + guild.playing = false + guild.paused = false + guild.skippers = [] + } + + // Auto-disconnect feature + if (guild.playing && guild.voiceChannel && guild.voiceChannel.id === oldState.channelID) { + if (guild.voiceChannel.members.filter(member => !member.user.bot).size < 1) { + guild.message.channel.send('Everyone has left my voice channel, the music will end in two minutes if no one rejoins.') + .then(msg => { + msg.delete({ timeout: 120000 }) + }) + + setTimeout(() => { + if (guild.dispatcher !== null && guild.voiceChannel.members.filter(member => !member.user.bot).size < 1) { + // Probably should be async? But no need here I think + guild.dispatcher.end('silent') + + guild.queue = [] + guild.playing = false + guild.paused = false + guild.dispatcher = null + guild.skippers = [] + + guild.message.channel.send('The music has ended because no one was listening to me ;~;') + } + }, 120000) + } + } + } +} diff --git a/src/modules/functions.js b/src/modules/functions.js index a30dd4b..d76e9fb 100644 --- a/src/modules/functions.js +++ b/src/modules/functions.js @@ -1,8 +1,3 @@ -const ytdl = require('ytdl-core-discord'); -const youtubeInfo = require('youtube-info'); -const getYoutubeId = require('get-youtube-id'); -const fetch = require('node-fetch'); - module.exports = client => { // Permission level function client.permlevel = message => { @@ -147,177 +142,6 @@ module.exports = client => { return client.users.cache.get(mention); } } - - - // MUSIC - client.music = {guilds: {}}; - - client.music.isYoutubeLink = function(input) { - return input.startsWith('https://www.youtube.com/') || input.startsWith('http://www.youtube.com/') || input.startsWith('https://youtube.com/') || input.startsWith('http://youtube.com/') || input.startsWith('https://youtu.be/') || input.startsWith('http://youtu.be/') || input.startsWith('http://m.youtube.com/') || input.startsWith('https://m.youtube.com/'); - } - - client.music.search = async function(query) - { - return new Promise(function(resolve, reject) - { - try{ - fetch("https://www.googleapis.com/youtube/v3/search?part=id&type=video&q=" + encodeURIComponent(query) + "&key=" + client.config.ytkey) - .then(res => res.json()) - .then(json => { - if(!json.items) { reject(); return; } - resolve(json.items[0]); - }); - } catch (err) { - client.logger.error("Music search err: ", err); - throw err; - }; - }); - } - - client.music.getGuild = function(id) - { - if(client.music.guilds[id]) return client.music.guilds[id]; - - return client.music.guilds[id] = - { - queue: [], - playing: false, - paused: false, - dispatcher: null, - skippers: [] - } - } - - client.music.getMeta = async function(id) - { - return new Promise(function(resolve, reject) - { - youtubeInfo(id, function(err, videoInfo) - { - if(err) throw err; - - resolve(videoInfo); - }); - }); - } - - client.music.play = async function(message, input, bypassQueue) - { - let voiceChannel = message.member.voice.channel; - if(!voiceChannel) return message.channel.send('<:error:466995152976871434> You need to be in a voice channel to use this command!'); - - let permissions = voiceChannel.permissionsFor(client.user); - if (!permissions.has('CONNECT')) { - return message.channel.send('<:error:466995152976871434> I do not have permission to join your voice channel.'); - } - if (!permissions.has('SPEAK')) { - return message.channel.send('<:error:466995152976871434> I do not have permission to join your voice channel.'); - } - if (voiceChannel.joinable != true) { - return message.channel.send("<:error:466995152976871434> I do not have permission to join your voice channel.") - } - - let id = undefined; - - if(client.music.isYoutubeLink(input)) - { - id = await getYoutubeId(input) - } else { - let item = await client.music.search(input); - if(!item) { - return message.channel.send(`<:error:466995152976871434> No results found.`); - }; - id = item.id.videoId; - } - - if(client.music.getGuild(message.guild.id).queue.length == 0 || bypassQueue) - { - let meta = await client.music.getMeta(id); - - if(!bypassQueue) client.music.getGuild(message.guild.id).queue.push({input: input, id: id, requestedBy: message.author, title: meta.title, author: meta.owner, thumbnail: meta.thumbnailUrl, duration: meta.duration}); - - let connection = await new Promise((resolve, reject) => - { - voiceChannel.join().then((connection) => - { - resolve(connection); - }); - }); - - function end(silent) - { - client.music.getGuild(message.guild.id).queue.shift(); - client.music.getGuild(message.guild.id).dispatcher = null; - - if(client.music.getGuild(message.guild.id).queue.length > 0) - { - client.music.play(message, client.music.getGuild(message.guild.id).queue[0].input, true); - } else { - client.music.getGuild(message.guild.id).playing = false; - - if(!silent) { - message.channel.send("<:play:467216788187512832> Queue is empty! Disconnecting from the voice channel."); - } - - connection.disconnect(); - } - } - - client.music.getGuild(message.guild.id).playing = true; - - let song = client.music.getGuild(message.guild.id).queue[0]; - - try - { - let dispatcher = client.music.getGuild(message.guild.id).dispatcher = connection.play(await ytdl("https://www.youtube.com/watch?v=" + id, {highWaterMark: 1024 * 1024 * 32}), {type: 'opus'}); - - dispatcher.on('finish', (a, b) => - { - end(a == "silent"); - }); - } catch(err) { - message.channel.send('<:error:466995152976871434> Failed to play **' + song.title + '** ' + err); - - end(); - } - - client.music.getGuild(message.guild.id).skippers = []; - message.channel.send(`<:play:467216788187512832> Now playing: **${song.title}**`); - } else { - let meta = await client.music.getMeta(id); - let song = {input: input, id: id, requestedBy: message.author, title: meta.title, author: meta.owner, thumbnail: meta.thumbnailUrl, duration: meta.duration}; - - client.music.getGuild(message.guild.id).queue.push(song); - - message.channel.send(`<:success:466995111885144095> Added to queue: **${song.title}**`); - } - } - - // MUSIC - TIMESTAMP - client.createTimestamp = function(duration){ - hrs = ~~(duration / 60 / 60), - min = ~~(duration / 60) % 60, - sec = ~~(duration - min * 60); - - if(String(hrs).length < 2) { - hrs = "0" + String(hrs) + ":"; - }; - - if(String(min).length < 2) { - min = "0" + String(min); - }; - - if(String(sec).length < 2) { - sec = "0" + String(sec); - }; - - if(hrs == "00:") { - hrs = ""; - } - - var time = hrs + min + ":" + sec; - return time; - }; //FIND ROLE client.findRole = function(input, message) { @@ -373,10 +197,9 @@ module.exports = client => { process.on("uncaughtException", err => { const errorMsg = err.stack.replace(new RegExp(`${__dirname}/`, "g"), "./"); client.logger.error(`Uncaught Exception: ${errorMsg}`); - process.exit(1); }); process.on("unhandledRejection", err => { - client.logger.error(`Unhandled rejection: ${err.stack}`); + client.logger.error(`Unhandled rejection: ${err}`); }); }; diff --git a/src/modules/music.js b/src/modules/music.js new file mode 100644 index 0000000..edf4c65 --- /dev/null +++ b/src/modules/music.js @@ -0,0 +1,256 @@ +// Copyright 2020 Emily J. / mudkipscience and contributors. Subject to the AGPLv3 license. + +const ytdl = require('ytdl-core') +const fetch = require('node-fetch') +const { MessageEmbed } = require('discord.js') +const { utc } = require('moment') + +exports.queue = {} + +exports.createTimestamp = function (s) { + if (s < 1) { + return 'LIVE' + } else if (s >= 3600) { + return utc(s * 1000).format('HH:mm:ss') + } else { + return utc(s * 1000).format('mm:ss') + } +} + +exports.getGuild = function (id) { + let guild = exports.queue[id] + + if (!guild) { + guild = {} + + guild.queue = [] + guild.playing = false + guild.paused = false + guild.dispatcher = null + guild.skippers = [] + guild.fixers = [] + + exports.queue[id] = guild + } + + return guild +} + +exports.getLinkFromID = function (id) { + return 'https://www.youtube.com/watch?v=' + id +} + +exports.getVideoByQuery = async function (client, query, message) { + let res + + try { + const id = await ytdl.getURLVideoID(query) + res = await fetch(`${client.config.endpoints.invidious}/api/v1/videos/${id}`) + } catch (err) { + res = await fetch(`${client.config.endpoints.invidious}/api/v1/search?q=${encodeURIComponent(query)}`) + } + + const parsed = await res.json().catch(function (e) { + return message.channel.send('<:error:466995152976871434> An error has occured: ' + e) + }) + + if (parsed) { + const videos = parsed + if (videos) { + return videos + } else { + return false + } + } else { + return false + } +} + +exports.play = async function (client, message, query, playNext, ignoreQueue) { + const guild = exports.getGuild(message.guild.id) + guild.message = message + + // message.channel.startTyping() + + if (!message.member.voice.channel && !guild.voiceChannel) { + message.channel.stopTyping() + return message.channel.send('<:error:466995152976871434> You have to be connected to a voice channel to use this command!') + } + + const vc = message.member.voice.channel + + let video + let videos + + if (!ignoreQueue) { + videos = await exports.getVideoByQuery(client, query, message) + if (!videos[1]) { + if (!videos[0]) { + video = videos + message.channel.stopTyping() + } else { + video = videos[0] + } + } + } + + if (videos || ignoreQueue) { + if (!ignoreQueue) { + // Fix the bot if somehow broken + // music "playing", nothing in queue + if ((guild.playing || guild.dispatcher) && guild.queue.length === 0) { + guild.queue = [] + guild.playing = false + guild.paused = false + guild.skippers = [] + guild.fixers = [] + guild.channel = null + // music not playing, something is in queue + } else if ((!guild.playing || !guild.dispatcher) && guild.queue.length > 0) { + guild.queue = [] + } + + if (!video) { + let output = '' + let i = 0 + for (i = 0; i < 5; i++) { + if (!videos[i]) break + output += `\`${i + 1}:\` **[${videos[i].title}](https://www.youtube.com/watch?v=${videos[i].videoId})** \`[${exports.createTimestamp(videos[i].lengthSeconds)}]\`\n` + } + + message.channel.stopTyping() + const embed = new MessageEmbed() + embed.setTitle('Please reply with a number `1-' + i + '` to select which song you want to add to the queue.') + embed.setColor(client.embedColour(message)) + embed.setDescription(output) + + let selection = await client.awaitReply(message, embed) + selection = Number(selection) + + switch (selection) { + case 1: + video = videos[0] + break + case 2: + if (videos[1]) { + video = videos[1] + } else { + return message.channel.send('<:error:466995152976871434> Invalid choice.') + } + break + case 3: + if (videos[2]) { + video = videos[2] + } else { + return message.channel.send('<:error:466995152976871434> Invalid choice.') + } + break + case 4: + if (videos[3]) { + video = videos[3] + } else { + return message.channel.send('<:error:466995152976871434> Invalid choice.') + } + break + case 5: + if (videos[4]) { + video = videos[4] + } else { + return message.channel.send('<:error:466995152976871434> Invalid choice.') + } + break + default: + return message.channel.send('<:error:466995152976871434> Invalid choice.') + } + } + + if (!video && videos[0]) { + video = videos[0] + } else if (!video) { + video = videos + } + + // Add video to queue + if (playNext === true) { + guild.queue.splice(1, 0, { video: video, requestedBy: message.author }) + } else { + guild.queue.push({ video: video, requestedBy: message.author }) + } + } + + // Figure out if the bot should add it to queue or play it right now + if (guild.playing) { + message.channel.send('<:success:466995111885144095> Queued **' + video.title + '** `[' + exports.createTimestamp(video.lengthSeconds) + ']`') + } else { + guild.playing = true + + guild.voiceChannel = vc + + if (!guild.channel) { + guild.channel = message.channel + } + + const connection = await vc.join() + + const v = guild.queue[0] + + try { + guild.dispatcher = connection.play(ytdl(v.video.videoId, { type: 'opus', bitrate: 'auto' })); + } catch (err) { + if (playNext && playNext === true) { + guild.queue.splice(1, 1) + } else { + guild.queue.pop() + } + + client.logger.error(err.stack) + return message.channel.send(`<:error:466995152976871434> An error has occured: \n\`${err}\``) + // return message.channel.send('<:error:466995152976871434> YouTube have made changes to their site that break Woomy\'s music module. An announcement will be made in the development server when this issue is resolved.') + } + guild.dispatcher.setVolume(0.25) + + guild.channel.send('<:player:467216674622537748> Now playing: **' + v.video.title + '** `[' + exports.createTimestamp(v.video.lengthSeconds) + ']`') + + // play next in queue on end + guild.dispatcher.on('error', (err) => { + console.error('[MUSIC ERROR] ' + String(err)); + }); + + guild.dispatcher.once('finish', () => { + guild.queue.shift() + guild.playing = false + + if (guild.queue.length > 0) { + exports.play(client, message, null, false, true) + } else { + guild.queue = [] + guild.playing = false + guild.paused = false + guild.skippers = [] + guild.fixers = [] + guild.channel = null + + connection.disconnect() + } + }) + } + } else { + return message.channel.send('failed to find the video!') + } +} + +exports.setVolume = function (guild, target) { + const g = exports.getGuild(guild.id) + + if (g.dispatcher) { + g.dispatcher.setVolume(target) + } +} + +exports.skip = function (guild, reason) { + const g = exports.getGuild(guild.id) + + if (g.dispatcher) { + g.dispatcher.end(reason) + } +} \ No newline at end of file diff --git a/version.json b/version.json index 6d6b7a3..8b6da90 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "number": "1.2.3", - "changelog": "**1.2.0 CHANGELOG:**\n> • Added action commands! (`cuddle`, `feed`, `hug`, `kiss`, `pat`, `poke`, `slap`, `smug`, `tickle`)\n> • Added `fact`\n> • Added `catfact`\n> • Added `dogfact`\n> • Added `yoda`\n> • Added `dice`\n> • Added `spoilerise`\n> • Added `zalgo`\n> • Added `dog`\n> • Added `cat`\n> • Added `lizard`\n> • Added `neko`\n> • Added `nekogif`\n> • Added `kemonomimi`\n> • Added `foxgirl`\n> • Added `identity`\n> • Added `pronouns`\n> • Added `sexuality`\n> • Added `ship`\n> • Renamed `flip to `coinflip` (flip remains as an alias)\n> • Renamed `math` to `calculate` (math is an alias)\n> • @Woomy is now a prefix\n> • Added the `inspire` alias to `inspirobot`\n> • Help now displays the amount of commands in each category\n> • Bots now get a badge in `userinfo`\n> • `roleinfo` now displays what permissions a role has\n> • small changes to `weather`\n> • Woomy now has clear logging of issues that prevent her from starting\n> • request npm module has been swapped out for node-fetch\n**NOTES:**\n> Thank you to Terryiscool160 for creating multiple commands used in this update" -} + "number": "1.4.8", + "changelog": "**1.4 Changelog**\n> • Splatoon commands have been added! check current and upcoming maps and modes with `~splatoonmaps`, current and upcoming salmon run maps, weapons and reward gear with `~salmonrun` and see what gear is on offer in the splatnet shop with `~splatnet`!\n**Notes:**\n> • Music is still broken and likely will be until v2 is released. Fixing v1 would delay v2 a lot, sorry >.<" +} \ No newline at end of file