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/LICENSE.md b/LICENSE.md index f82dfa5..4e230a1 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,7 @@ MIT License -Copyright (c) 2020 mudkipscience +Copyright (c) 2018 YorkAARGH +Copyright (c) 2018-2020 mudkipscience Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 530097c..e37522d 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ # 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. +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://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. -Self hosting is generally not recommended, but instructions are provided below if you still wish to do so. Woomy's code will need to be modified before it will run on your machine. +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 - node.js v12.0.0 or higher - node-gyp build tools -- ffmpeg +- ffmpeg (or ffmpeg-static) # Installation - Clone Woomy to your machine @@ -18,4 +18,4 @@ Self hosting is generally not recommended, but instructions are provided below i - Open config.js in your code editor and insert all the required information # Contributing -If you wish to contribute to Woomy, please fork the repository and open a pull request. +If you wish to contribute to Woomy, please fork the repository and open a pull request. Any contribution is appreciated <3 diff --git a/configTemplate.js b/configTemplate.js index a8adf42..2c516a0 100644 --- a/configTemplate.js +++ b/configTemplate.js @@ -1,68 +1,79 @@ const config = { - // ID's - "owners": ["433790467830972417", "324937993972350976"], - - // Tokens - "token": "", - "devtoken": "", - "ytkey": "", - "dblkey": "", - - // Default per-server settings - "defaultSettings" : { - "prefix": "~", - "devprefix": "!", - "modRole": "None set", - "adminRole": "None set", - "mutedRole": "None set", - "autorole": "off", - "welcomeChannel": "off", - "welcomeMessage": "off", - "leaveMessage": "off", - "chatlogsChannel": "off", - "modlogsChannel": "off", - "raidMode": "off", - "raidModeStrict": "off", - "blacklisted": "ARRAY", - "botChannels": "ARRAY", - "AFK": "ARRAY", - "SAR": "ARRAY" + // ID's + "owners": [], // Adding your ID here will give you access to dangerous commands like eval. Please be careful with who you add here! Eval can be used to modify the host machine. + + // Host options + "devmodeEnabled": false, // true or false + "loggingServer": "", // server ID, or blank to disable + "startupLogs": "", // Channel ID, or blank to disable + "consoleLogs": "", // Channel ID, or blank to disable + + // Tokens + "token": "", // Your bot's token. + "devtoken": "", // (optional) another token, meant for a bot used for development + "dblkey": "", // (optional) top.gg key, sends bot statistics to top.gg. You do not need this. + + // Configurable API endpoints + endpoints: { + invidious: 'https://invidio.us/api/' + }, + + // Default per-server settings + "defaultSettings" : { + "prefix": "~", + "devprefix": "!", + "modRole": "None set", + "adminRole": "None set", + "mutedRole": "None set", + "autorole": "off", + "welcomeChannel": "off", + "welcomeMessage": "off", + "leaveMessage": "off", + "chatlogsChannel": "off", + "modlogsChannel": "off", + "raidMode": "off", + "raidModeStrict": "off", + "blacklisted": "ARRAY", + "botChannels": "ARRAY", + "AFK": "ARRAY", + "SAR": "ARRAY", + "customCommands": "ARRAY", + }, + + // Perm levels + permLevels: [ + { level: 0, + name: "User", + check: () => true }, - - // Perm levels - permLevels: [ - { level: 0, - name: "User", - check: () => true - }, - - { level: 1, - name: "Moderator", - check: (message) => { - try { - if (message.member.roles.has(message.settings.modRole)) return true; - } catch (e) { - return false; - } + + { level: 1, + name: "Moderator", + check: (message) => { + try { + if (message.member.roles.cache.has(message.settings.modRole)) return true; + } catch (e) { + return false; } - }, - - { level: 2, - name: "Administrator", - check: (message) => { - try { - if (message.member.roles.has(message.settings.adminRole)) return true; - } catch (e) { - return false; - } + } + }, + + { level: 2, + name: "Administrator", + check: (message) => { + try { + if (message.member.roles.cache.has(message.settings.adminRole) || message.member.permissions.has("ADMINISTRATOR")) return true; + } catch (e) { + return false; } - }, - - { level: 3, - name: "Server Owner", - check: (message) => message.channel.type === "text" ? (message.guild.ownerID === message.author.id ? true : false) : false - }, - ] - }; - - module.exports = config; \ No newline at end of file + } + }, + + { level: 3, + name: "Server Owner", + check: (message) => message.channel.type === "text" ? (message.guild.ownerID === message.author.id ? true : false) : false + }, + ] +}; + +module.exports = config; diff --git a/index.js b/index.js index 9b559ff..49d5326 100644 --- a/index.js +++ b/index.js @@ -1,28 +1,57 @@ +if (Number(process.version.slice(1).split(".")[0]) < 12) { + throw new Error("Node 12.0.0 or higher is required. Please update Node on your system."); +}; + const Discord = require('discord.js'); const { promisify } = require('util'); const readdir = promisify(require('fs').readdir); const Enmap = require('enmap'); const chalk = require('chalk'); -const DBL = require("dblapi.js"); const client = new Discord.Client(); -client.config = require('./config'); -client.version = require('./version.json'); -client.logger = require('./src/modules/Logger'); -require("./src/modules/functions")(client); +try { + client.config = require('./config'); +} catch (err) { + console.log('Failed to load config.js:', err); + process.exit(); +}; + +try{ + client.version = require('./version.json'); +} catch (err) { + console.log('Failed to load version.json:', err); + process.exit(); +}; + +try{ + client.logger = require('./src/modules/Logger'); +} catch (err) { + console.log('Failed to load Logger.js:', err); + process.exit(); +}; + client.logger.setClient(client); -if(process.env['USER'] != 'container') { +try{ + require("./src/modules/functions")(client); +} catch (err) { + console.log('Failed to load functions.js:', err); + process.exit(); +}; + +if(client.config.devmodeEnabled == true && process.env['USER'] != 'container') { client.devmode = true; } else { client.devmode = false; - const dblapi = new DBL(client.config.dblkey, client); -} + if(client.config.dblkey.length > 0) { + const DBL = require("dblapi.js"); + const dblapi = new DBL(client.config.dblkey, client); + }; +}; client.commands = new Enmap(); client.aliases = new Enmap(); client.settings = new Enmap({name: 'settings'}); -client.blacklist = new Enmap({name: 'blacklist'}); const init = async () => { const cmdFiles = await readdir("./src/commands/"); diff --git a/package.json b/package.json index 7210610..73e312c 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,29 @@ { "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.2.1", "better-sqlite3": "^5.4.1", - "chalk": "^3.0.0", + "chalk": "^4.0.0", "dblapi.js": "^2.3.1", - "discord.js": "^12.0.1", + "discord.js": "^12.0.2", "enmap": "^5.2.4", "garfield": "^1.1.2", - "get-youtube-id": "^1.0.1", "hastebin-gen": "^2.0.5", "moment": "^2.24.0", "moment-duration-format": "^2.3.2", + "nekos.life": "^2.0.5", + "node-fetch": "^2.6.0", "prism-media": "^1.2.1", "randomcolor": "^0.5.4", - "request": "^2.88.2", "relevant-urban": "^2.0.0", + "request": "^2.88.2", + "to-zalgo": "^1.0.1", "urban": "^0.3.2", - "url-unshort": "^5.0.0", - "url-unshorten": "^1.0.6", "weather-js": "^2.0.0", - "youtube-info": "^1.3.2", - "ytdl-core-discord": "^1.1.0" + "ytdl-core-discord": "^1.2.0" }, "devDependencies": {}, "scripts": { diff --git a/resources/images/attackhelicopter.jpg b/resources/images/attackhelicopter.jpg new file mode 100644 index 0000000..c077f0d Binary files /dev/null and b/resources/images/attackhelicopter.jpg differ diff --git a/resources/other/coolpeople.json b/resources/other/coolpeople.json new file mode 100644 index 0000000..d5f335c --- /dev/null +++ b/resources/other/coolpeople.json @@ -0,0 +1,21 @@ +{ + "coolPeople": [ + "448354605617643520", + "433790467830972417", + "231777839576252417", + "285992938314661899", + "231704701433937931", + "324937993972350976", + "336492042299637771", + "273867501006225419", + "331870539897372672", + "304000458144481280", + "239787232666451980", + "264970229514371072", + "254310746450690048", + "358390849807319040", + "211011138656272386", + "266472557740425216", + "102943767346057216" + ] +} \ No newline at end of file diff --git a/resources/other/genders.json b/resources/other/genders.json deleted file mode 100644 index 3b50663..0000000 --- a/resources/other/genders.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "cisgender": "Someone who identifies with their assigned gender at birth.", - "transgender": "Someone who identifies with a gender that is not their assigned gender at birth." -} \ No newline at end of file diff --git a/resources/other/identities.json b/resources/other/identities.json new file mode 100644 index 0000000..4afd11a --- /dev/null +++ b/resources/other/identities.json @@ -0,0 +1,110 @@ +{ + "agender": { + "name": "agender", + "description": "A gender identity used by someone who has no gender, or sometimes by someone whose gender is neutral." + }, + "androgyne": { + "name": "androgyne", + "description": "A gender identity associated with androgyny. Androgynes have a gender which is simultaneously feminine and masculine, although not necessarily in equal amounts." + }, + "androgynous": { + "name": "androgynous", + "description": "A term used to refer to people who have both feminine and masculine characteristics." + }, + "aporagender": { + "name": "aporagender", + "description": "A gender that is not male, female, or anything in between that still has a distinct gendered feeling." + }, + "bigender": { + "name": "bigender", + "description": "Having two gender identities, either simultaneously or varying between the two." + }, + "cisgender": { + "name": "cisgender", + "description": "Someone who identifies with their assigned gender at birth." + }, + "demiboy": { + "name": "demiboy", + "description": "Someone who identifies as partially male and partially another gender." + }, + "demiflux": { + "name": "demiflux", + "description": "A gender where one part of someone’s gender is static, and the other part fluctuates in intensity." + }, + "demigender": { + "name": "demigender", + "description": "Someone who identifies as partially one gender, and partially another." + }, + "demigirl": { + "name": "demigirl", + "description": "Someone who identifies as partially female and partially another gender." + }, + "dyadic": { + "name": "dyadic", + "description": "A word used to refer to people who are not intersex" + }, + "enby": { + "name": "enby", + "description": "Shortened term for “nonbinary”. Used as a noun, like “boy” or “girl” but for nonbinary people." + }, + "fluidflux": { + "name": "fluidflux", + "description": "A gender identity which refers to someone with a gender that moves between two or more genders and also fluctuates in intensity." + }, + "genderfluid": { + "name": "genderfluid", + "description": "Someone whose gender varies over time. This might be fluctuating between different genders, or expressing multiple aspects of various genders at the same time." + }, + "genderflux": { + "name": "genderflux", + "description": "Someone whose gender fluctuates, usually between agender and something else." + }, + "genderqueer": { + "name": "genderqueer", + "description": "An umbrella term for all the nonbinary genders. Genderqueer can be a standalone identity, or can refer to a more specific gender identity." + }, + "gendervoid": { + "name": "gendervoid", + "description": "Someone who does not experience gender, or who feels an absence or void in the place of gender." + }, + "intersex": { + "name": "intersex", + "description": "An intersex person is someone with sex characteristics (sexual anatomy, reproductive organs, chromosomal patterns, etc.) that do not align with the typical descriptions of male and female." + }, + "libragender": { + "name": "libragender", + "description": "A gender identity that is mostly agender, but has a connection to masculinity and/or femininity and/or other gendered feelings. That connection may be static (libragender, librafeminine, libramasculine, etc) or fluid, where one feels that the gender one experiences changes (librafluid)." + }, + "neutrois": { + "name": "neutrois", + "description": "Having a null or neutral gender." + }, + "nonbinary": { + "name": "nonbinary", + "description": "An umbrella term for all the gender identities that aren't male or female (the binary genders)" + }, + "non-gendered": { + "name": "non-gendered", + "description": "Having no gender." + }, + "polygender": { + "name": "polygender", + "description": "Having more than one gender, either at the same time or at different times. A polygender person may identify with any combination of binary and nonbinary genders." + }, + "pangender": { + "name": "pangender", + "description": "Having more than one gender, especially someone who identifies as all genders." + }, + "transfeminine": { + "name": "transfeminine", + "description": "A transgender person who identifies with femininity, either as a binary female or a fem-leaning nonbinary identity." + }, + "transgender": { + "name": "transgender", + "description": "Someone whose identity differs from their assigned gender at birth." + }, + "transmasculine": { + "name": "transmasculine", + "description": "A transgender person who identifies with masculinity, either as a binary male or a masc-leaning nonbinary identity." + } +} \ No newline at end of file diff --git a/resources/other/pronouns.json b/resources/other/pronouns.json index 544b7b4..fa65ece 100644 --- a/resources/other/pronouns.json +++ b/resources/other/pronouns.json @@ -1,3 +1,154 @@ { - + "ae/aer": { + "name": "ae/aer", + "examples": "• **Ae** went to the park.\n• I went with **aer**.\n• **Ae** brought **aer** frisbee.\n• At least I think it was **aers.**\n• **Ae** threw a frisbee to **aerself**." + }, + "e/em": { + "name": "e/em", + "examples": "• **E** went to the park.\n• I went with **em**.\n• **E** brought **eir** frisbee.\n• At least I think it was **eirs**.\n• **E** threw a frisbee to **emself**." + }, + "ey/em": { + "name": "ey/em", + "examples": "• **Ey** went to the park.\n• I went with **em**.\n• **Ey** brought **eir** frisbee.\n• At least I think it was **eirs**.\n• **Ey** threw a frisbee to **eirself**." + }, + "fae/faer": { + "name": "fae/faer", + "examples": "• **Fae** went to the park.\n• I went with **faer**.\n• **Fae** brought **faer** frisbee.\n• At least I think it was **faers**.\n• **Fae** threw a frisbee to **faerself**." + }, + "fey/fem": { + "name": "fey/fem", + "examples": "• **Fey** went to the park.\n• I went with **fem**.\n• **Fey** brought **feir** frisbee.\n• At least I think it was **feirs**.\n• **Fey** threw a frisbee to **feirself**." + }, + "he/him": { + "name": "he/him", + "examples": "• **He** went to the park.\n• I went with **him**.\n• **He** brought **his** frisbee.\n• At least I think it was **his**.\n• **He** threw a frisbee to **himself**." + }, + "hu/hum": { + "name": "hu/hum", + "examples": "• **Hu** went to the park.\n• I went with **hum**.\n• **Hu** brought **hus** frisbee.\n• At least I think it was **hus**.\n• **Hu** threw a frisbee to **humself**." + }, + "it/its": { + "name": "it/its", + "examples": "• **It** went to the park.\n• I went with **it**.\n• **It** brought **its** frisbee.\n• At least I think it was **its**.\n• **It** threw a frisbee to **itself**." + }, + "jee/jem": { + "name": "je/jem", + "examples": "• **Jee** went to the park.\n• I went with **jem**.\n• **Jee** brought **jeir** frisbee.\n• At least I think it was **jeirs**.\n• **Jee** threw a frisbee to **jemself**." + }, + "kit/kits": { + "name": "kit/kits", + "examples": "• **Kit** went to the park.\n• I went with **kit**.\n• **Kit** brought **kits** frisbee.\n• At least I think it was **kits**.\n• **Kit** threw a frisbee to **kitself**." + }, + "ne/nem": { + "name": "ne/nem", + "examples": "• **Ne** went to the park.\n• I went with **nem**.\n• **Ne** brought **nir** frisbee.\n• At least I think it was **nirs**.\n• **Ne** threw a frisbee to **nemself**." + }, + "peh/pehm": { + "name": "pe/pehm", + "examples": "• **Peh** went to the park.\n• I went with **pehm**.\n• **Peh** brought **peh's** frisbee.\n• At least I think it was **peh's**.\n• **Peh** threw a frisbee to **pehself**." + }, + "per": { + "name": "per/per", + "examples": "• **Per** went to the park.\n• I went with **per**.\n• **Per** brought **per** frisbee.\n• At least I think it was **pers**.\n• **Per** threw a frisbee to **perself**." + }, + "sie/hir": { + "name": "sie/hir", + "examples": "• **Sie** went to the park.\n• I went with **hir**.\n• **Sie** brought **hir** frisbee.\n• At least I think it was **hirs**.\n• **Sie* threw a frisbee to **hirself**." + }, + "se/sim": { + "name": "se/sim", + "examples": "• **Se** went to the park.\n• I went with **sim**.\n• **Se** brought **ser** frisbee.\n• At least I think it was **sers**.\n• **Se** threw a frisbee to **serself**." + }, + "shi/hir": { + "name": "shi/her", + "examples": "• **Shi** went to the park.\n• I went with **hir**.\n• **Shi** brought **shir** frisbee.\n• At least I think it was **hirs**.\n• **Shi** threw a frisbee to **hirself**." + }, + "si/hyr": { + "name": "si/hyr", + "examples": "• **Si** went to the park.\n• I went with **hyr**.\n• **Si** brought **hyr** frisbee.\n• At least I think it was **hyrs**.\n• **Si** threw a frisbee to **hyrself**." + }, + "they/them": { + "name": "they/them", + "examples": "• **They** went to the park.\n• I went with **them**.\n• **They** brought **their** frisbee.\n• At least I think it was **theirs**.\n• **They** threw a frisbee to **themself**." + }, + "thon": { + "name": "thon/thon", + "examples": "• **Thon** went to the park.\n• I went with **thon**.\n• **Thon** brought **thons** frisbee.\n• At least I think it was **thons**.\n• **Thon** threw a frisbee to **thonself**." + }, + "she/her": { + "name": "she/her", + "examples": "• **She** went to the park.\n• I went with **her**.\n• **She** brought **her** frisbee.\n• At least I think it was **hers**.\n• **She** threw a frisbee to **herself**." + }, + "ve/ver": { + "name": "ve/ver", + "examples": "• **Ve** went to the park.\n• I went with **ver**.\n• **Ve** brought **vis** frisbee.\n• At least I think it was **vis**.\n• **Ve** threw a frisbee to **verself**." + }, + "ve/vem": { + "name": "ve/vem", + "examples": "• **Ve** went to the park.\n• I went with **vem**.\n• **Ve** brought **vir** frisbee.\n• At least I think it was **virs**.\n• **Ve** threw a frisbee to **vemself**." + }, + "vi/ver": { + "name": "vi/ver", + "examples": "• **Vi** went to the park.\n• I went with **ver**.\n• **Vi** brought **ver** frisbee.\n• At least I think it was **vers**.\n• **Vi** threw a frisbee to **verself**." + }, + "vi/vim/vir": { + "name": "vi/vim/vir", + "examples": "• **Vi** went to the park.\n• I went with **vim**.\n• **Vi** brought **vir** frisbee.\n• At least I think it was **virs**.\n• **Vi** threw a frisbee to **vimself**." + }, + "vi/vim/vim": { + "name": "vi/vim/vim", + "examples": "• **Vi** went to the park.\n• I went with **vim**.\n• **Vi** brought **vim** frisbee.\n• At least I think it was **vims**.\n• **Vi** threw a frisbee to **vimself**." + }, + "xie/xer": { + "name": "xie/xer", + "examples": "• **Xie** went to the park.\n• I went with **xer**.\n• **Xie** brought **xer** frisbee.\n• At least I think it was **xers**.\n• **Xie** threw a frisbee to **xerself**." + }, + "xe/xem": { + "name": "xe/xem", + "examples": "• **Xe** went to the park.\n• I went with **xem**.\n• **Xe** brought **xyr** frisbee.\n• At least I think it was **xyrs**.\n• **Xe** threw a frisbee to **xemself**." + }, + "xey/xem": { + "name": "xey/xem", + "examples": "• **Xey** went to the park.\n• I went with **xem**.\n• **Xey** brought **xeir** frisbee.\n• At least I think it was **xeirs**.\n• **Xey** threw a frisbee to **xemself**." + }, + "yo": { + "name": "yo", + "examples": "• **Yo** went to the park.\n• I went with **yo**.\n• **Yo** brought **yos** frisbee.\n• At least I think it was **yos**.\n• **Yo** threw a frisbee to **yosself**." + }, + "ze/hir": { + "name": "ze/hir", + "examples": "• **Ze** went to the park.\n• I went with **hir**.\n• **Ze** brought **hir** frisbee.\n• At least I think it was **hirs**.\n• **Ze** threw a frisbee to **hirself**." + }, + "ze/zem": { + "name": "ze/zem", + "examples": "• **Ze** went to the park.\n• I went with **zem**.\n• **Ze** brought **zes** frisbee.\n• At least I think it was **zes**.\n• **Ze** threw a frisbee to **zirself**." + }, + "ze/mer": { + "name": "ze/mer", + "examples": "• **Ze** went to the park.\n• I went with **mer**.\n• **Ze** brought **zer** frisbee.\n• At least I think it was **zers**.\n• **Ze** threw a frisbee to **zemself**." + }, + "zee/zed": { + "name": "zee/zed", + "examples": "• **Zee** went to the park.\n• I went with **zed**.\n• **Zee** brought **zeta** frisbee.\n• At least I think it was **zetas**.\n• **Zee** threw a frisbee to **zedself**." + }, + "ze/zir": { + "name": "ze/zir", + "examples": "• **Ze** went to the park.\n• I went with **zir**.\n• **Ze** brought **zir** frisbee.\n• At least I think it was **zirs**.\n• **Ze** threw a frisbee to **zirself**." + }, + "zie/zir": { + "name": "zie/zir", + "examples": "• **Zie** went to the park.\n• I went with **zir**.\n• **Zie** brought **zir** frisbee.\n• At least I think it was **zirs**.\n• **Zie** threw a frisbee to **zirself**." + }, + "zie/zem": { + "name": "zie/zem", + "examples": "• **Zie** went to the park.\n• I went with **zem**.\n• **Zie** brought **zes** frisbee.\n• At least I think it was **zes**.\n• **Zie** threw a frisbee to **zirself**." + }, + "zie/hir": { + "name": "zie/hir", + "examples": "• **Zie** went to the park.\n• I went with **hir**.\n• **Zie** brought **hir** frisbee.\n• At least I think it was **hirs**.\n• **Zie** threw a frisbee to **hirself**." + }, + "zme/zmyr": { + "name": "zme/zmyr", + "examples": "• **Zme** went to the park.\n• I went with **zmyr**.\n• **Zme** brought **zmyr** frisbee.\n• At least I think it was **zmyrs**.\n• **Zme** threw a frisbee to **zmyrself**." + } } \ No newline at end of file diff --git a/resources/other/sexualities.json b/resources/other/sexualities.json index 544b7b4..a8805ed 100644 --- a/resources/other/sexualities.json +++ b/resources/other/sexualities.json @@ -1,3 +1,102 @@ { - + "aesthetic attraction": { + "name": "aesthetic attraction", + "description": "Getting pleasure from the appearance of a specific individual like you would from watching beautiful scenery." + }, + "platonic attraction": { + "name": "platonic attraction", + "description": "A desire to have a platonic relationship or friendship with a specific individual." + }, + "romantic attraction": { + "name": "romantic attraction", + "description": "A desire to have a romantic relationship with a specific individual." + }, + "sensual attraction": { + "name": "sensual attraction", + "description": "A desire for physical intimacy with a specific individual. Cuddling, hand holding, etc." + }, + "sexual attraction": { + "name": "sexual attraction", + "description": "A desire for sexual contact with a specific individual." + }, + "abrosexual": { + "name": "abrosexual", + "description": "Someone whose sexuality changes frequently. They experience different sexualities over time." + }, + "androgynosexual": { + "name": "androgynosexual", + "description": "Someone who is attracted to both men and women, particularly those who are androgynous in appearance." + }, + "androsexual": { + "name": "androsexual", + "description": "Someone who is attracted to men and/or masculine people." + }, + "aromantic": { + "name": "aromantic", + "description": "Someone who does not experience romantic attraction. They do not have to also be asexual however as they may still experience other types of attraction." + }, + "asexual": { + "name": "asexual", + "description": "Someone who feels little to no sexual attraction to anyone." + }, + "bisexual": { + "name": "bisexual", + "description": "Someone who is attracted to more than one gender." + }, + "ceterosexual": { + "name": "ceterosexual", + "description": "Someone who is attracted to non-binary people." + }, + "demisexual": { + "name": "demisexual", + "description": "Someone who doesn't experience sexual/romantic attraction towards someone until they form an emotional connection." + }, + "finsexual": { + "name": "finsexual", + "description": "Someone who is attracted to women and/or feminine people." + }, + "gay": { + "name": "gay", + "description": "Someone who is homosexual (attracted to ones own gender)" + }, + "gynosexual": { + "name": "gynosexual", + "description": "Someone who as attracted to women and/or feminity." + }, + "grey-romantic": { + "name": "grey-romantic", + "description": "Someone with a romantic orentiation that is somewhere between aromantic and romantic." + }, + "heterosexual": { + "name": "heterosexual", + "description": "Someone who is attracted to people of the opposite gender." + }, + "homosexual": { + "name": "homosexual", + "description": "Someone attracted to people of ones own gender." + }, + "lesbian": { + "name": "lesbian", + "description": "A woman who is attracted to other women." + }, + "omnisexual": { + "name": "omnisexual", + "description": "Someone who is attracted to all genders." + }, + "pansexual": { + "name": "pansexual", + "description": "Someone who is attracted towards people regardless of their gender identity." + }, + "pomosexual": { + "name": "pomosexual", + "description": "Someone who does not fit into any sexual orientation label." + }, + "polysexual": { + "name": "polysexual", + "description": "Someone who is attracted to some, but not all genders." + }, + "straight": { + "name": "straight", + "description": "Someone who is heterosexual (attracted to people of the opposite gender)" + } } \ No newline at end of file diff --git a/src/commands/about.js b/src/commands/about.js index 5c1538f..2049bfa 100644 --- a/src/commands/about.js +++ b/src/commands/about.js @@ -26,9 +26,9 @@ exports.run = (client, message) => { } embed = new Discord.MessageEmbed(); - embed.setTitle(`Woomy`); embed.setColor(client.embedColour(message)); embed.setThumbnail(client.user.avatarURL({format: "png", dynamic: true, size: 2048})) + embed.setTitle("About Woomy") embed.addField( "General:", `• users: \`${client.users.cache.size}\`\n• channels: \`${client.channels.cache.size}\`\n• servers: \`${client.guilds.cache.size}\`\n• commands: \`${client.commands.size}\`\n• uptime: \`${duration}\``,true ); @@ -45,7 +45,7 @@ exports.run = (client, message) => { exports.conf = { enabled: true, guildOnly: false, - aliases: ["stats"], + aliases: ["stats", "botinfo"], permLevel: "User", requiredPerms: [] }; 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/math.js b/src/commands/calculate.js similarity index 92% rename from src/commands/math.js rename to src/commands/calculate.js index 05f0d78..e7737b3 100644 --- a/src/commands/math.js +++ b/src/commands/calculate.js @@ -29,14 +29,14 @@ exports.run = (client, message, args) => { exports.conf = { enabled: true, guildOnly: false, - aliases: ["calculate", "calc"], + aliases: ["calc", "math"], permLevel: "User", requiredPerms: [] }; exports.help = { - name: "math", + name: "calculate", category: "Utility", description: "Solves basic mathematical equations.", - usage: "math [equation]" + usage: "calculate [equation]" }; \ No newline at end of file diff --git a/src/commands/cat.js b/src/commands/cat.js new file mode 100644 index 0000000..afd627f --- /dev/null +++ b/src/commands/cat.js @@ -0,0 +1,30 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message) => { + message.channel.startTyping(); + try { + sfw.meow().then((json) => { + message.channel.send(json.url) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("cat.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: "cat", + category: "Image", + description: "Sends you cat pics.", + usage: "cat" +}; diff --git a/src/commands/catfact.js b/src/commands/catfact.js new file mode 100644 index 0000000..366999e --- /dev/null +++ b/src/commands/catfact.js @@ -0,0 +1,27 @@ +const fetch = require("node-fetch") +exports.run = async (bot, message, args) => { + message.channel.startTyping(); + try{ + 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}`); + }; + message.channel.stopTyping(); +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["kittenfact"], + permLevel: "User", + requiredPerms: [] + }; + + exports.help = { + name: "catfact", + category: "Fun", + description: "Sends a fun fact about a cat.", + usage: "catfact/kittenfact" + }; diff --git a/src/commands/flip.js b/src/commands/coinflip.js similarity index 90% rename from src/commands/flip.js rename to src/commands/coinflip.js index 99ba778..418a996 100644 --- a/src/commands/flip.js +++ b/src/commands/coinflip.js @@ -22,14 +22,14 @@ exports.run = (client, message, args) => { exports.conf = { enabled: true, guildOnly: false, - aliases: [], + aliases: ["flip"], permLevel: "User", requiredPerms: [] }; exports.help = { - name: "flip", + name: "coinflip", category: "Fun", description: "Flips a coin!", - usage: "flip [heads/tails]" + usage: "coinflip [heads/tails]" }; diff --git a/src/commands/credits.js b/src/commands/credits.js index b3f8516..5eb48ce 100644 --- a/src/commands/credits.js +++ b/src/commands/credits.js @@ -1,6 +1,6 @@ exports.run = async (client, message, args) => { message.channel.send( - `**Credits:**\n• \`mudkipscience#8904\` and \`FLGX#9896\`for developing the bot\n• \`An Idiots Guide\` for the Guidebot bot base\n• \`dellannie#6057\` for helping with the music commands\n• \`TheCakeChicken#9088\` and \`Tina the Cyclops girl#0064\` for helping me not suck at coding\n• \`AirVentTrent\` for the icon, find him on Instagram` + `__**Credits:**__\n• \`mudkipscience#8904\`, \`FLGX#9896\` and \`TheCakeChicken#9088\` for developing the bot\n• \`An Idiots Guide\` for the Guidebot bot base\n• \`Tina the Cyclops girl#0064\` for helping me not suck at coding\n• \`AirVentTrent\` for the icon, find him on Instagram\n• \`Terryiscool160\` for contributing to Woomy.` ); }; @@ -14,7 +14,7 @@ exports.conf = { exports.help = { name: "credits", - category: "Miscellaneous", + category: "Utility", description: "Cool people", usage: "credits" }; diff --git a/src/commands/cuddle.js b/src/commands/cuddle.js new file mode 100644 index 0000000..63263ae --- /dev/null +++ b/src/commands/cuddle.js @@ -0,0 +1,69 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't say who you wanted to cuddle! Usage: \`${client.commands.get(`cuddle`).help.usage}\``) + }; + + var people = ""; + + for (var i = 0; i < args.length; i++) { + var user = client.getUserFromMention(args[i]) + if (user) { + user = message.guild.members.cache.get(user.id).displayName; + } else { + users = client.searchForMembers(message.guild, args[i]); + if (users.length > 1) + return message.channel.send( + "<:error:466995152976871434> Found multiple users for `" + args[i] + "`, Please be more specific or mention the user instead." + ); + else if (users.length == 0) + return message.channel.send( + "<:error:466995152976871434> That user doesn't seem to exist. Try again!" + ); + user = users[0].displayName; + }; + if(i+1 == args.length && args.length > 1) { + people += `**and** ${user}!` + } else if(args.length < 2) { + people += `${user}!`; + } else if(args.length == 2 && i == 0) { + people += `${user} `; + } else { + people += `${user}, `; + }; + }; + + + + message.channel.startTyping(); + try { + sfw.cuddle().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + embed.setDescription(`**${message.guild.members.cache.get(message.author.id).displayName}** cuddled **${people}**`) + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("cuddle.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "cuddle", + category: "Action", + description: "cuddle someone!", + usage: "cuddle [@user/user] (you can cuddle as many people as you want!)" +}; diff --git a/src/commands/dice.js b/src/commands/dice.js new file mode 100644 index 0000000..be9edcf --- /dev/null +++ b/src/commands/dice.js @@ -0,0 +1,27 @@ +exports.run = async (bot, message, args) => { + if (args.length === 0) { + message.channel.send(`🎲 You rolled a ${Array.from(Array(6).keys()).random() + 1}!`); + } else { + if (args[0].match(/^\d+$/)) { + message.channel.send(`🎲 You rolled a ${Array.from(Array(parseInt(args[0])).keys()).random() + 1}!`); + } else { + message.channel.send(`🎲 You rolled a ${Array.from(Array(6).keys()).random() + 1}!`); + } + } + }; + + exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["diceroll", "roll"], + permLevel: "User", + requiredPerms: [] + }; + + exports.help = { + name: "dice", + category: "Fun", + description: "Rolls a dice.", + usage: "dice " + }; + diff --git a/src/commands/dog.js b/src/commands/dog.js new file mode 100644 index 0000000..39ce25c --- /dev/null +++ b/src/commands/dog.js @@ -0,0 +1,30 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message) => { + message.channel.startTyping(); + try { + sfw.woof().then((json) => { + message.channel.send(json.url) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("dog.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: "dog", + category: "Image", + description: "Sends you dog pics.", + usage: "dog" +}; diff --git a/src/commands/dogfact.js b/src/commands/dogfact.js new file mode 100644 index 0000000..fca8a62 --- /dev/null +++ b/src/commands/dogfact.js @@ -0,0 +1,27 @@ +const fetch = require("node-fetch"); +exports.run = async (bot, message, args) => { + message.channel.startTyping(); + 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]}`)); + } catch(err) { + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`); + }; + message.channel.stopTyping(); +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["pupfact"], + permLevel: "User", + requiredPerms: [] + }; + + exports.help = { + name: "dogfact", + category: "Fun", + description: "Sends a fun fact about a doggo.", + usage: "dogfact/pupfact" + }; diff --git a/src/commands/emoji.js b/src/commands/emoji.js index 795686c..ab6907b 100644 --- a/src/commands/emoji.js +++ b/src/commands/emoji.js @@ -11,7 +11,6 @@ exports.run = async (client, message, args) => { format = ".gif" }; - console.log(string.length) if(string.length > 18) { ID = string.slice(string.length - 18); } else { diff --git a/src/commands/emojify.js b/src/commands/emojify.js index 058faa3..46dfa35 100644 --- a/src/commands/emojify.js +++ b/src/commands/emojify.js @@ -34,7 +34,7 @@ exports.run = (client, message, args) => { if(emojified.length > 2000) { return message.channel.send("<:error:466995152976871434> The emojified message exceeds 2000 characters.") - } + }; message.channel.send(emojified); }; diff --git a/src/commands/fact.js b/src/commands/fact.js new file mode 100644 index 0000000..0e351a6 --- /dev/null +++ b/src/commands/fact.js @@ -0,0 +1,30 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message) => { + message.channel.startTyping(); + try { + sfw.fact().then((json) => { + message.channel.send("__**Did you know?**__\n" + json.fact + "."); + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("fact.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["randomfact"], + permLevel: "User", + requiredPerms: [] +}; + +exports.help = { + name: "fact", + category: "Fun", + description: "Sends you a random fact.", + usage: "fact" +}; diff --git a/src/commands/feed.js b/src/commands/feed.js new file mode 100644 index 0000000..9749f45 --- /dev/null +++ b/src/commands/feed.js @@ -0,0 +1,69 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't say who you wanted to feed! Usage: \`${client.commands.get(`feed`).help.usage}\``) + }; + + var people = ""; + + for (var i = 0; i < args.length; i++) { + var user = client.getUserFromMention(args[i]) + if (user) { + user = message.guild.members.cache.get(user.id).displayName; + } else { + users = client.searchForMembers(message.guild, args[i]); + if (users.length > 1) + return message.channel.send( + "<:error:466995152976871434> Found multiple users for `" + args[i] + "`, Please be more specific or mention the user instead." + ); + else if (users.length == 0) + return message.channel.send( + "<:error:466995152976871434> That user doesn't seem to exist. Try again!" + ); + user = users[0].displayName; + }; + if(i+1 == args.length && args.length > 1) { + people += `**and** ${user}!` + } else if(args.length < 2) { + people += `${user}!`; + } else if(args.length == 2 && i == 0) { + people += `${user} `; + } else { + people += `${user}, `; + }; + }; + + + + message.channel.startTyping(); + try { + sfw.feed().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + embed.setDescription(`**${message.guild.members.cache.get(message.author.id).displayName}** fed **${people}**`) + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("feed.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "feed", + category: "Action", + description: "feed someone!", + usage: "feed [@user/user] (you can feed as many people as you want!)" +}; diff --git a/src/commands/feedback.js b/src/commands/feedback.js index 9f4e436..6b4512b 100644 --- a/src/commands/feedback.js +++ b/src/commands/feedback.js @@ -22,7 +22,7 @@ exports.conf = { exports.help = { name: "feedback", - category: "Miscellaneous", + category: "Utility", description: "Send feedback to my developer.", usage: "feedback [message]" }; diff --git a/src/commands/fixmusic.js b/src/commands/fixmusic.js new file mode 100644 index 0000000..387aa9f --- /dev/null +++ b/src/commands/fixmusic.js @@ -0,0 +1,30 @@ +const { getGuild } = require('../modules/music') +module.exports.run = async (client, message, args, level) =>{ + guild = getGuild(message.guild.id) + + guild.queue = [] + guild.playing = false + guild.paused = false + guild.skippers = [] + + if (guild.dispatcher) { + guild.dispatcher.end('silent') + } + + message.channel.send('<:success:466995111885144095> Music has been fixed (hopefully)') +} + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "Moderator", + 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..0271d8e 100644 --- a/src/commands/forceskip.js +++ b/src/commands/forceskip.js @@ -1,11 +1,16 @@ +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') + + message.channel.send('<:success:466995111885144095> Song skipped.') }; exports.conf = { @@ -13,7 +18,7 @@ exports.conf = { guildOnly: true, aliases: [], permLevel: "Moderator", - requiredPerms: ["SPEAK"] + requiredPerms: [] }; exports.help = { @@ -22,7 +27,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 new file mode 100644 index 0000000..3d8a0e6 --- /dev/null +++ b/src/commands/foxgirl.js @@ -0,0 +1,30 @@ +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: false, + 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/help.js b/src/commands/help.js index 80fe504..945eeae 100644 --- a/src/commands/help.js +++ b/src/commands/help.js @@ -4,6 +4,7 @@ exports.run = (client, message, args, level) => { var ran = false; var output = ""; + var commands = 0; var prefix; var currentCategory; @@ -14,7 +15,7 @@ exports.run = (client, message, args, level) => { }; if(!args[0]) { - embed.setTitle("Command list"); + embed.setTitle(`Command list`); embed.setDescription(`⁣For more information on a specific command use \`${prefix}help \`\nFor the full command list use \`${prefix}help all\`\n`); const myCommands = message.guild ? client.commands.filter( @@ -34,19 +35,21 @@ exports.run = (client, message, args, level) => { ); sorted.forEach( c => { - const cat = c.help.category.toProperCase(); + const cat = c.help.category; if (currentCategory !== cat) { if(ran == true) { - embed.addField(currentCategory + ":", output.slice(0, -6)) + embed.addField(currentCategory + ` [${commands}]`, output) output = ""; + commands = 0; } currentCategory = cat; ran = true } - output += `\`${prefix}${c.help.name}\`**,** `; + output += `\`${c.help.name}\` `; + commands = commands + 1; }); - embed.addField(currentCategory + ":", output.slice(0, -6)); + embed.addField(currentCategory + ` [${commands}]`, output); embed.addField( "Invite me", @@ -65,7 +68,7 @@ exports.run = (client, message, args, level) => { }; if(args[0].toLowerCase() == "all") { - embed.setTitle("Command list"); + embed.setTitle(`Command list`); embed.setDescription(`⁣For more information on a specific command use \`${prefix}help \`\nFor the full command list use \`${prefix}help all\`\n`); const myCommands = client.commands @@ -81,19 +84,22 @@ exports.run = (client, message, args, level) => { ); sorted.forEach( c => { - const cat = c.help.category.toProperCase(); + const cat = c.help.category; if (currentCategory !== cat) { if(ran == true) { - embed.addField(currentCategory + ":", output.slice(0, -6)) + embed.addField(currentCategory + ` [${commands}]`, output) output = ""; + commands = 0; } currentCategory = cat; ran = true } - output += `\`${prefix}${c.help.name}\`**,** `; + output += `\`${c.help.name}\` `; + commands = commands + 1; }); - embed.addField(currentCategory + ":", output.slice(0, -6)); + + embed.addField(currentCategory + ` [${commands}]`, output); embed.addField( "Invite me", diff --git a/src/commands/hug.js b/src/commands/hug.js new file mode 100644 index 0000000..30ef214 --- /dev/null +++ b/src/commands/hug.js @@ -0,0 +1,69 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't say who you wanted to hug! Usage: \`${client.commands.get(`hug`).help.usage}\``) + }; + + var people = ""; + + for (var i = 0; i < args.length; i++) { + var user = client.getUserFromMention(args[i]) + if (user) { + user = message.guild.members.cache.get(user.id).displayName; + } else { + users = client.searchForMembers(message.guild, args[i]); + if (users.length > 1) + return message.channel.send( + "<:error:466995152976871434> Found multiple users for `" + args[i] + "`, Please be more specific or mention the user instead." + ); + else if (users.length == 0) + return message.channel.send( + "<:error:466995152976871434> That user doesn't seem to exist. Try again!" + ); + user = users[0].displayName; + }; + if(i+1 == args.length && args.length > 1) { + people += `**and** ${user}!` + } else if(args.length < 2) { + people += `${user}!`; + } else if(args.length == 2 && i == 0) { + people += `${user} `; + } else { + people += `${user}, `; + }; + }; + + + + message.channel.startTyping(); + try { + sfw.hug().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + embed.setDescription(`**${message.guild.members.cache.get(message.author.id).displayName}** hugged **${people}**`) + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("hug.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "hug", + category: "Action", + description: "Hug someone!", + usage: "hug [@user/user] (you can hug as many people as you want!)" +}; diff --git a/src/commands/identity.js b/src/commands/identity.js new file mode 100644 index 0000000..b7f3809 --- /dev/null +++ b/src/commands/identity.js @@ -0,0 +1,36 @@ +const identities = require ("../../resources/other/identities.json"); +exports.run = async (client, message, args) => { + var output = ""; + if(!args[0]) { + for (var key of Object.keys(identities)) { + output += `${key}, ` + }; + return message.channel.send(`__**Identities**__\n${output.slice(0, -2)}`); + } else { + if(args.join(" ").toLowerCase() == "attack helicopter" || args.join(" ").toLowerCase() == "apache attack helicopter" || args.join(" ").toLowerCase() == "apache") { + return message.channel.send({ + files: [new Discord.MessageAttachment("./resources/images/attackhelicopter.jpg")] + }); + } + output = identities[args.join(" ").toLowerCase()]; + if(!output) { + return message.channel.send("<:error:466995152976871434> No results for that query."); + }; + return message.channel.send(`__**${output.name.toProperCase()}**__\n${output.description}`); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["identities"], + permLevel: "User", + requiredPerms: [] +}; + +exports.help = { + name: "identity", + category: "Fun", + description: "Gives you information about the specified identity.", + usage: "identity [identity]" +}; diff --git a/src/commands/inspirobot.js b/src/commands/inspirobot.js index 6a26ee7..17a028f 100644 --- a/src/commands/inspirobot.js +++ b/src/commands/inspirobot.js @@ -1,26 +1,21 @@ -const request = require('request') +const fetch = require("node-fetch") exports.run = async (client, message) => { message.channel.startTyping(); - request({ - url: "http://inspirobot.me/api?generate=true" - }, - function(error, res, body) { - if(body.length > 0) { - message.channel.send({ - files: [new Discord.MessageAttachment(body)] - }); - message.channel.stopTyping(); - } else { - message.channel.send('<:error:466995152976871434> API error, please retry.') - message.channel.stopTyping(); - }; - }); + try { + fetch('http://inspirobot.me/api?generate=true') + .then(res => res.text()) + .then(body => message.channel.send({files: [new Discord.MessageAttachment(body)]})); + 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: [], + aliases: ["inspire"], permLevel: "User", requiredPerms: [] }; diff --git a/src/commands/kemonomimi.js b/src/commands/kemonomimi.js new file mode 100644 index 0000000..7d16938 --- /dev/null +++ b/src/commands/kemonomimi.js @@ -0,0 +1,30 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message) => { + message.channel.startTyping(); + try { + sfw.kemonomimi().then((json) => { + message.channel.send(json.url) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("kemonomimi.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: false, + guildOnly: false, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "kemonomimi", + category: "Image", + description: "Sends you pictures of people with animal characteristics.", + usage: "kemonomimi" +}; diff --git a/src/commands/kiss.js b/src/commands/kiss.js new file mode 100644 index 0000000..495ee62 --- /dev/null +++ b/src/commands/kiss.js @@ -0,0 +1,69 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't say who you wanted to kiss! Usage: \`${client.commands.get(`kiss`).help.usage}\``) + }; + + var people = ""; + + for (var i = 0; i < args.length; i++) { + var user = client.getUserFromMention(args[i]) + if (user) { + user = message.guild.members.cache.get(user.id).displayName; + } else { + users = client.searchForMembers(message.guild, args[i]); + if (users.length > 1) + return message.channel.send( + "<:error:466995152976871434> Found multiple users for `" + args[i] + "`, Please be more specific or mention the user instead." + ); + else if (users.length == 0) + return message.channel.send( + "<:error:466995152976871434> That user doesn't seem to exist. Try again!" + ); + user = users[0].displayName; + }; + if(i+1 == args.length && args.length > 1) { + people += `**and** ${user}!` + } else if(args.length < 2) { + people += `${user}!`; + } else if(args.length == 2 && i == 0) { + people += `${user} `; + } else { + people += `${user}, `; + }; + }; + + + + message.channel.startTyping(); + try { + sfw.kiss().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + embed.setDescription(`**${message.guild.members.cache.get(message.author.id).displayName}** kissed **${people}**`) + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("kiss.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "kiss", + category: "Action", + description: "Kiss someone!", + usage: "kiss [@user/user] (you can kiss as many people as you want!)" +}; diff --git a/src/commands/lizard.js b/src/commands/lizard.js new file mode 100644 index 0000000..f476704 --- /dev/null +++ b/src/commands/lizard.js @@ -0,0 +1,30 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message) => { + message.channel.startTyping(); + try { + sfw.lizard().then((json) => { + message.channel.send(json.url) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("lizard.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "lizard", + category: "Image", + description: "Sends pictures of lizards.", + usage: "lizard" +}; 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/neko.js b/src/commands/neko.js new file mode 100644 index 0000000..6e62f19 --- /dev/null +++ b/src/commands/neko.js @@ -0,0 +1,30 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message) => { + message.channel.startTyping(); + try { + sfw.neko().then((json) => { + message.channel.send(json.url); + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("neko.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: false, + guildOnly: false, + aliases: ["catgirl"], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "neko", + category: "Image", + description: "Sends you pictures of catgirls.", + usage: "neko" +}; diff --git a/src/commands/nekogif.js b/src/commands/nekogif.js new file mode 100644 index 0000000..cc878e8 --- /dev/null +++ b/src/commands/nekogif.js @@ -0,0 +1,30 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message) => { + message.channel.startTyping(); + try { + sfw.nekoGif().then((json) => { + message.channel.send(json.url); + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("nekogif.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: false, + guildOnly: false, + aliases: ["catgirlgif"], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "nekogif", + category: "Image", + description: "Sends you gifs of catgirls.", + usage: "nekogif" +}; diff --git a/src/commands/nowplaying.js b/src/commands/nowplaying.js index e94f71a..c26222d 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(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/owoify.js b/src/commands/owoify.js index ae8b9a5..9587a6f 100644 --- a/src/commands/owoify.js +++ b/src/commands/owoify.js @@ -13,8 +13,8 @@ exports.run = (client, message, args) => { owoified = owoified.replace(/!+/g, ' ' + faces[~~(Math.random() * faces.length)] + ' ') if(owoified.length > 2000) { - return message.channel.send("<:error:466995152976871434> The owoified message exceeds 2000 characters.") - } + owoified = owoified.slice(0, -Math.abs(owoified.length - 2000)) + }; message.channel.send(owoified) }; diff --git a/src/commands/pat.js b/src/commands/pat.js new file mode 100644 index 0000000..fee469b --- /dev/null +++ b/src/commands/pat.js @@ -0,0 +1,69 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't say who you wanted to pat! Usage: \`${client.commands.get(`pat`).help.usage}\``) + }; + + var people = ""; + + for (var i = 0; i < args.length; i++) { + var user = client.getUserFromMention(args[i]) + if (user) { + user = message.guild.members.cache.get(user.id).displayName; + } else { + users = client.searchForMembers(message.guild, args[i]); + if (users.length > 1) + return message.channel.send( + "<:error:466995152976871434> Found multiple users for `" + args[i] + "`, Please be more specific or mention the user instead." + ); + else if (users.length == 0) + return message.channel.send( + "<:error:466995152976871434> That user doesn't seem to exist. Try again!" + ); + user = users[0].displayName; + }; + if(i+1 == args.length && args.length > 1) { + people += `**and** ${user}!` + } else if(args.length < 2) { + people += `${user}!`; + } else if(args.length == 2 && i == 0) { + people += `${user} `; + } else { + people += `${user}, `; + }; + }; + + + + message.channel.startTyping(); + try { + sfw.pat().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + embed.setDescription(`**${message.guild.members.cache.get(message.author.id).displayName}** patted **${people}**`) + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("pat.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: ["headpat"], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "pat", + category: "Action", + description: "pat someone!", + usage: "pat [@user/user] (you can pat as many people as you want!)" +}; 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..882aedd 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: 'playnext [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/poke.js b/src/commands/poke.js new file mode 100644 index 0000000..07c48bc --- /dev/null +++ b/src/commands/poke.js @@ -0,0 +1,69 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't say who you wanted to poke! Usage: \`${client.commands.get(`poke`).help.usage}\``) + }; + + var people = ""; + + for (var i = 0; i < args.length; i++) { + var user = client.getUserFromMention(args[i]) + if (user) { + user = message.guild.members.cache.get(user.id).displayName; + } else { + users = client.searchForMembers(message.guild, args[i]); + if (users.length > 1) + return message.channel.send( + "<:error:466995152976871434> Found multiple users for `" + args[i] + "`, Please be more specific or mention the user instead." + ); + else if (users.length == 0) + return message.channel.send( + "<:error:466995152976871434> That user doesn't seem to exist. Try again!" + ); + user = users[0].displayName; + }; + if(i+1 == args.length && args.length > 1) { + people += `**and** ${user}!` + } else if(args.length < 2) { + people += `${user}!`; + } else if(args.length == 2 && i == 0) { + people += `${user} `; + } else { + people += `${user}, `; + }; + }; + + + + message.channel.startTyping(); + try { + sfw.poke().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + embed.setDescription(`**${message.guild.members.cache.get(message.author.id).displayName}** poked **${people}**`) + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("poke.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "poke", + category: "Action", + description: "poke someone!", + usage: "poke [@user/user] (you can poke as many people as you want!)" +}; diff --git a/src/commands/prefix.js b/src/commands/prefix.js index 774a22b..1d72e74 100644 --- a/src/commands/prefix.js +++ b/src/commands/prefix.js @@ -1,10 +1,4 @@ exports.run = async (client, message, args) => { - if(client.devmode === true) { - return message.channel.send( - "<:error:466995152976871434> This command has been disabled because Woomy is in development mode." - ); - }; - const settings = message.settings; if (!client.settings.has(message.guild.id)) client.settings.set(message.guild.id, {}); diff --git a/src/commands/pronoun.js b/src/commands/pronoun.js new file mode 100644 index 0000000..0d4b36a --- /dev/null +++ b/src/commands/pronoun.js @@ -0,0 +1,36 @@ +const pronouns = require ("../../resources/other/pronouns.json"); +exports.run = async (client, message, args) => { + var output = ""; + if(!args[0]) { + for (var key of Object.keys(pronouns)) { + output += `${key}, ` + }; + return message.channel.send(`__**Pronouns:**__\n${output.slice(0, -2)}`); + } else { + if(args.join(" ").toLowerCase() == "attack helicopter" || args.join(" ").toLowerCase() == "apache attack helicopter" || args.join(" ").toLowerCase() == "apache") { + return message.channel.send({ + files: [new Discord.MessageAttachment("./resources/images/attackhelicopter.jpg")] + }); + }; + output = pronouns[args.join(" ").toLowerCase()]; + if(!output) { + return message.channel.send("<:error:466995152976871434> No results for that query."); + }; + return message.channel.send(`__**Example sentences using ${output.name}:**__\n${output.examples}`); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["pronouns"], + permLevel: "User", + requiredPerms: [] +}; + +exports.help = { + name: "pronoun", + category: "Fun", + description: "Gives you information on how to use the specified pronoun.", + usage: "pronoun [pronoun]" +}; 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/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/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..968e08a 100644 --- a/src/commands/rip.js +++ b/src/commands/rip.js @@ -1,12 +1,15 @@ -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}`); + }; + message.channel.stopTyping(); } exports.conf = { diff --git a/src/commands/roleinfo.js b/src/commands/roleinfo.js index f6fd0e0..10d8c4d 100644 --- a/src/commands/roleinfo.js +++ b/src/commands/roleinfo.js @@ -15,17 +15,47 @@ exports.run = async (client, message, args, level) => { return message.channel.send(`<:error:466995152976871434> Role not found.`) } - if(role.hoist === true) { - var hoist = `Yes` - } else { - var hoist = `No` - } + var permissions = "```"; + if(role.permissions.has("ADMINISTRATOR")) permissions += "ADMINISTRATOR, "; + if(role.permissions.has("CREATE_INSTANT_INVITE")) permissions += "CREATE_INSTANT_INVITE, "; + if(role.permissions.has("KICK_MEMBERS")) permissions += "KICK_MEMBERS, "; + if(role.permissions.has("BAN_MEMBERS")) permissions += "BAN_MEMBERS, "; + if(role.permissions.has("MANAGE_CHANNELS")) permissions += "MANAGE_CHANNELS, "; + if(role.permissions.has("MANAGE_GUILD")) permissions += "MANAGE_GUILD, "; + if(role.permissions.has("ADD_REACTIONS")) permissions += "ADD_REACTIONS, "; + if(role.permissions.has("VIEW_AUDIT_LOG")) permissions += "VIEW_AUDIT_LOG, "; + if(role.permissions.has("PRIORITY_SPEAKER")) permissions += "PRIORITY_SPEAKER, "; + if(role.permissions.has("STREAM")) permissions += "STREAM, "; + if(role.permissions.has("VIEW_CHANNEL")) permissions += "VIEW_CHANNEL, "; + if(role.permissions.has("SEND_MESSAGES")) permissions += "SEND_MESSAGES, "; + if(role.permissions.has("SEND_TTS_MESSAGES")) permissions += "SEND_TTS_MESSAGES, "; + if(role.permissions.has("MANAGE_MESSAGES")) permissions += "MANAGE_MESSAGES, "; + if(role.permissions.has("EMBED_LINKS")) permissions += "EMBED_LINKS, "; + if(role.permissions.has("ATTACH_FILES")) permissions += "ATTACH_FILES, "; + if(role.permissions.has("READ_MESSAGE_HISTORY")) permissions += "READ_MESSAGE_HISTORY, "; + if(role.permissions.has("MENTION_EVERYONE")) permissions += "MENTION_EVERYONE, "; + if(role.permissions.has("USE_EXTERNAL_EMOJIS")) permissions += "USE_EXTERNAL_EMOJIS, "; + if(role.permissions.has("CONNECT")) permissions += "CONNECT, "; + if(role.permissions.has("SPEAK")) permissions += "SPEAK, "; + if(role.permissions.has("MUTE_MEMBERS")) permissions += "MUTE_MEMBERS, "; + if(role.permissions.has("DEAFEN_MEMBERS")) permissions += "DEAFEN_MEMBERS, "; + if(role.permissions.has("MOVE_MEMBERS")) permissions += "MOVE_MEMBERS, "; + if(role.permissions.has("USE_VAD")) permissions += "USE_VAD, "; + if(role.permissions.has("CHANGE_NICKNAME")) permissions += "CHANGE_NICKNAME, "; + if(role.permissions.has("MANAGE_NICKNAMES")) permissions += "MANAGE_NICKNAMES, "; + if(role.permissions.has("MANAGE_ROLES")) permissions += "MANAGE_ROLES, "; + if(role.permissions.has("MANAGE_WEBHOOKS")) permissions += "MANAGE_WEBHOOKS, "; + if(role.permissions.has("MANAGE_EMOJIS")) permissions += "MANAGE_EMOJIS, "; + permissions = permissions.slice(0, -2); + permissions += "```"; var embed = new Discord.MessageEmbed(); - embed.setColor(role.color) + embed.setColor(role.color); + embed.setTitle(role.name); embed.setDescription( - `• **Name:** ${role.name}\n• **ID:** ${role.id}\n• **Hex:** ${role.hexColor}\n• **Members:** ${role.members.size}\n• **Position:** ${role.position}\n• **Hoisted:** ${hoist}` + `• **ID:** ${role.id}\n• **Hex:** ${role.hexColor}\n• **Members:** ${role.members.size}\n• **Position:** ${role.position}\n• **Hoisted:** ${role.hoist}` ); + embed.addField(`**Permissions:**`, permissions) message.channel.send(embed) }; diff --git a/src/commands/say.js b/src/commands/say.js index e196847..e63fc48 100644 --- a/src/commands/say.js +++ b/src/commands/say.js @@ -5,7 +5,7 @@ exports.run = (client, message, args, level) => { ); }; if (message.content.includes("@everyone")) { - return message.channel.send(`<@${message.author.id}>`); + return message.channel.send(message.author); }; 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/sexuality.js b/src/commands/sexuality.js new file mode 100644 index 0000000..c41930b --- /dev/null +++ b/src/commands/sexuality.js @@ -0,0 +1,36 @@ +const sexualities = require ("../../resources/other/sexualities.json"); +exports.run = async (client, message, args) => { + var output = ""; + if(!args[0]) { + for (var key of Object.keys(sexualities)) { + output += `${key}, ` + }; + return message.channel.send(`__**Sexualities:**__\n${output.slice(0, -2)}`); + } else { + if(args.join(" ").toLowerCase() == "attack helicopter" || args.join(" ").toLowerCase() == "apache attack helicopter" || args.join(" ").toLowerCase() == "apache") { + return message.channel.send({ + files: [new Discord.MessageAttachment("./resources/images/attackhelicopter.jpg")] + }); + } + output = sexualities[args.join(" ").toLowerCase()]; + if(!output) { + return message.channel.send("<:error:466995152976871434> No results for that query."); + }; + return message.channel.send(`__**${output.name.toProperCase()}:**__\n${output.description}`); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["sexualities"], + permLevel: "User", + requiredPerms: [] +}; + +exports.help = { + name: "sexuality", + category: "Fun", + description: "Gives you information about the specified sexuality.", + usage: "sexuality [sexuality]" +}; diff --git a/src/commands/ship.js b/src/commands/ship.js new file mode 100644 index 0000000..cd62b5d --- /dev/null +++ b/src/commands/ship.js @@ -0,0 +1,67 @@ +const Discord = require('discord.js') +exports.run = async (client, message, args) => { + var rating = Math.floor(Math.random() * 100) + 1 + var meter = ['▬', '▬', '▬', '▬', '▬', '▬', '▬', '▬', '▬'] + var hearts = [ + '❤️', + '🧡', + '💛', + '💚', + '💙', + '💜' + ] + + if (!args[0]) { + return message.channel.send(client.userError(exports, 'Missing argument, the `name1` argument is required!')) + } + + if (!args[1]) { + return message.channel.send(client.userError(exports, 'Missing argument, the `name2` argument is required!')) + } + + const firstName = args[0] + const secondName = args[1] + + const shipName = firstName.substr(0, firstName.length * 0.5) + secondName.substr(secondName.length * 0.5) + + if (shipName.toLowerCase() === 'teily' || shipName.toLowerCase() === 'emrra') { + rating = '100' + } + + var pos = 0 + var under = 9 + while (pos < 10) { + if (rating < under) { + meter.splice(pos, 0, hearts.random()) + break + } + pos++ + under += 10 + } + + 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, + guildOnly: false, + aliases: [], + permLevel: "User", + requiredPerms: [] +}; + +exports.help = { + name: "ship", + category: "Fun", + description: "Ship two people together <3", + 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..35ee02f 100644 --- a/src/commands/skip.js +++ b/src/commands/skip.js @@ -1,49 +1,50 @@ -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') + + 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') 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 +52,7 @@ exports.conf = { guildOnly: true, aliases: ["voteskip"], permLevel: "User", - requiredPerms: ["SPEAK"] + requiredPerms: [] }; exports.help = { diff --git a/src/commands/slap.js b/src/commands/slap.js new file mode 100644 index 0000000..c87c783 --- /dev/null +++ b/src/commands/slap.js @@ -0,0 +1,69 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't say who you wanted to slap! Usage: \`${client.commands.get(`slap`).help.usage}\``) + }; + + var people = ""; + + for (var i = 0; i < args.length; i++) { + var user = client.getUserFromMention(args[i]) + if (user) { + user = message.guild.members.cache.get(user.id).displayName; + } else { + users = client.searchForMembers(message.guild, args[i]); + if (users.length > 1) + return message.channel.send( + "<:error:466995152976871434> Found multiple users for `" + args[i] + "`, Please be more specific or mention the user instead." + ); + else if (users.length == 0) + return message.channel.send( + "<:error:466995152976871434> That user doesn't seem to exist. Try again!" + ); + user = users[0].displayName; + }; + if(i+1 == args.length && args.length > 1) { + people += `**and** ${user}!` + } else if(args.length < 2) { + people += `${user}!`; + } else if(args.length == 2 && i == 0) { + people += `${user} `; + } else { + people += `${user}, `; + }; + }; + + + + message.channel.startTyping(); + try { + sfw.slap().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + embed.setDescription(`**${message.guild.members.cache.get(message.author.id).displayName}** slapped **${people}**`) + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("slap.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "slap", + category: "Action", + description: "Slap someone >:3", + usage: "slap [@user/user] (you can slap as many people as you want!)" +}; diff --git a/src/commands/smug.js b/src/commands/smug.js new file mode 100644 index 0000000..011f2bd --- /dev/null +++ b/src/commands/smug.js @@ -0,0 +1,33 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message) => { + message.channel.startTyping(); + try { + sfw.smug().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("smug.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "smug", + category: "Action", + description: "Sends a smug gif.", + usage: "smug" +}; diff --git a/src/commands/songinfo.js b/src/commands/songinfo.js new file mode 100644 index 0000000..cd7bb0b --- /dev/null +++ b/src/commands/songinfo.js @@ -0,0 +1,42 @@ +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] + + const embed = new MessageEmbed() + embed.setThumbnail(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/spoilerise.js b/src/commands/spoilerise.js new file mode 100644 index 0000000..25ab221 --- /dev/null +++ b/src/commands/spoilerise.js @@ -0,0 +1,28 @@ +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't provide any text! Usage: \`${client.commands.get(`spoiler`).help.usage}\``) + }; + + var output = `||${[...message.cleanContent.substring(9)].join("||||")}||`; + + if(output.length > 2000) { + output = output.slice(0, -Math.abs(output.length - 2000)) + }; + + message.channel.send(output) +}; + +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["spoilerize", "spoiler"], + permLevel: "User", + requiredPerms: [] +}; + +exports.help = { + name: "spoilerise", + category: "Fun", + description: "Spoilers every letter in the provided text.", + usage: "spoiler [text]" +}; diff --git a/src/commands/stop.js b/src/commands/stop.js index 695dd04..b36b2a1 100644 --- a/src/commands/stop.js +++ b/src/commands/stop.js @@ -1,18 +1,18 @@ -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 = [] - message.channel.send("<:stop:467639381390262284> Playback stopped!"); + message.channel.send('<:success:466995111885144095> Playback stopped!') }; exports.conf = { diff --git a/src/commands/support.js b/src/commands/support.js index 8fad6b2..c15de88 100644 --- a/src/commands/support.js +++ b/src/commands/support.js @@ -12,7 +12,7 @@ exports.conf = { exports.help = { name: "support", - category: "utility", + category: "Utility", description: "Sends a link to Woomy's support/development server.", usage: "support" }; diff --git a/src/commands/tickle.js b/src/commands/tickle.js new file mode 100644 index 0000000..e75a771 --- /dev/null +++ b/src/commands/tickle.js @@ -0,0 +1,69 @@ +const API = require('nekos.life'); +const {sfw} = new API(); +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't say who you wanted to tickle! Usage: \`${client.commands.get(`tickle`).help.usage}\``) + }; + + var people = ""; + + for (var i = 0; i < args.length; i++) { + var user = client.getUserFromMention(args[i]) + if (user) { + user = message.guild.members.cache.get(user.id).displayName; + } else { + users = client.searchForMembers(message.guild, args[i]); + if (users.length > 1) + return message.channel.send( + "<:error:466995152976871434> Found multiple users for `" + args[i] + "`, Please be more specific or mention the user instead." + ); + else if (users.length == 0) + return message.channel.send( + "<:error:466995152976871434> That user doesn't seem to exist. Try again!" + ); + user = users[0].displayName; + }; + if(i+1 == args.length && args.length > 1) { + people += `**and** ${user}!` + } else if(args.length < 2) { + people += `${user}!`; + } else if(args.length == 2 && i == 0) { + people += `${user} `; + } else { + people += `${user}, `; + }; + }; + + + + message.channel.startTyping(); + try { + sfw.tickle().then((json) => { + embed = new Discord.MessageEmbed(); + embed.setImage(json.url) + embed.setColor(client.embedColour(message)); + embed.setDescription(`**${message.guild.members.cache.get(message.author.id).displayName}** tickled **${people}**`) + message.channel.send(embed) + message.channel.stopTyping(); + }); + } catch (err) { + client.logger.error("tickle.js: " + err); + message.channel.send(`<:error:466995152976871434> An error has occurred: ${err}`) + message.channel.stopTyping(); + }; +}; + +exports.conf = { + enabled: true, + guildOnly: true, + aliases: [], + permLevel: "User", + requiredPerms: ["EMBED_LINKS"] +}; + +exports.help = { + name: "tickle", + category: "Action", + description: "Tickle someone!", + usage: "tickle [@user/user] (you can tickle as many people as you want!)" +}; diff --git a/src/commands/userinfo.js b/src/commands/userinfo.js index 7514de5..98d6738 100644 --- a/src/commands/userinfo.js +++ b/src/commands/userinfo.js @@ -1,18 +1,10 @@ const Discord = require("discord.js"); - +const coolPeople = require('../../resources/other/coolpeople.json') exports.run = (client, message, args) => { - var user; - var guild; - var nick = ""; - var roles = ""; - var presence = ""; - var badges = ""; - var status; - var createdAt; - var avurl; - var tag; - var id; - var bot; + var user, guild, status, createdAt, avurl, tag, id; + var nick = "", roles = "", presence = "", badges = ""; + var coolPerson = false; + var friendos = coolPeople.coolPeople; if(message.guild) { user = message.mentions.members.first(); @@ -39,16 +31,27 @@ exports.run = (client, message, args) => { nick = `\n• **Nickname:** ${user.nickname}`; }; - if(user.user.id == message.guild.ownerID) { - badges = "<:owner:685703193694306331>\n" + for (var i = 0; i < friendos.length; i++) { + if (user.user.id == friendos[i]) + coolPerson = true; + }; + + if(coolPerson == true) { + badges += "🌟" + } + + if(user.user.id == message.guild.ownerID) { + badges += "<:owner:685703193694306331>" + } + + if(user.user.bot) { + badges += "<:bot:686489601678114859>" + } + + + if(badges.length > 0) { + badges += "\n" } - - createdTimestamp = user.user.createdTimestamp; - var date = new Date(createdTimestamp * 1000); - var hours = date.getHours(); - var minutes = "0" + date.getMinutes(); - var seconds = "o" + date.getSeconds(); - console.log(date) user.roles.cache.forEach((role) => { roles = roles + role.name + "`, `" 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/weather.js b/src/commands/weather.js index f5fc362..ef01bc8 100644 --- a/src/commands/weather.js +++ b/src/commands/weather.js @@ -5,46 +5,47 @@ exports.run = async (client, message, args, error) => { `<:error:466995152976871434> You didn't give me a location. Usage: \`${client.commands.get(`weather`).help.usage}\`` ); }; - - if(args.join(" ").toLowerCase() == "antarctica") { - return; - } message.channel.startTyping(); - - weather.find({search: args.join(" "), degreeType: 'C'}, function(err, result) { - if(err) client.logger.log(`weather.js error: ${JSON.stringify(error)}`, "error") - if(result.length < 2 || !result) { + + try { + weather.find({search: args.join(" "), degreeType: 'C'}, function(err, result) { + if(err) return message.channel.send(`<:error:466995152976871434> API error: \`${error}\``) + if(result.length < 2 || !result) { + message.channel.stopTyping(); + return message.channel.send("<:error:466995152976871434> City not found!"); + }; + + var location = result[0].location; + var current = result[0].current; + + var warning = (`${location.alert}` || "No warnings"); + + var embedColour; + if (current.temperature < 0) { + embedColour = "#addeff"; + }else if (current.temperature < 20) { + embedColour = "#4fb8ff"; + }else if (current.temperature < 26) { + embedColour = "#ffea4f"; + }else if (current.temperature < 31) { + embedColour = "#ffa14f" + } else { + embedColour = "#ff614f" + }; + + embed = new Discord.MessageEmbed(); + embed.setAuthor(`Weather for ${location.name}:`) + embed.setDescription(`• **Condition:** ${current.skytext}\n• **Temperature:** ${current.temperature}°C\n• **Feels like:** ${current.feelslike}°C\n• **Humidity:** ${current.humidity}%\n• **Wind:** ${current.winddisplay}\n• **Warnings:** ${warning}`) + embed.setThumbnail(current.imageUrl) + embed.setFooter(`Last updated at ${current.observationtime} ${current.date}`) + embed.setColor(embedColour) message.channel.stopTyping(); - return message.channel.send("<:error:466995152976871434> City not found!"); - }; - - var location = result[0].location; - var current = result[0].current; - - var warning = (`${location.alert}` || "No warnings"); - - var embedColour; - if (current.temperature < 0) { - embedColour = "#addeff"; - }else if (current.temperature < 20) { - embedColour = "#4fb8ff"; - }else if (current.temperature < 26) { - embedColour = "#ffea4f"; - }else if (current.temperature < 31) { - embedColour = "#ffa14f" - } else { - embedColour = "#ff614f" - }; - - embed = new Discord.MessageEmbed(); - embed.addField(`Weather for ${location.name}:`, `**Condition:** ${current.skytext}\n**Temperature:** ${current.temperature}C°\n**Feels like:** ${current.feelslike}C°\n**Humidity:** ${current.humidity}%\n**Wind:** ${current.winddisplay}\n**Warnings:** ${warning}`) - embed.setThumbnail(current.imageUrl) - embed.setFooter(`Last updated at ${current.observationtime} ${current.date}`) - embed.setColor(embedColour) - message.channel.stopTyping(); - message.channel.send(embed) - }); + message.channel.send(embed) + }); + } catch(err) { + return message.channel.send(`<:error:466995152976871434> API error: \`${err}\``) + }; }; exports.conf = { diff --git a/src/commands/yoda.js b/src/commands/yoda.js new file mode 100644 index 0000000..496b929 --- /dev/null +++ b/src/commands/yoda.js @@ -0,0 +1,33 @@ + +const fetch = require("node-fetch") +exports.run = async (client, message, args) => { + const speech = args.join(' '); + if (!speech) { + return message.channel.send(`<:error:466995152976871434> Please include text for me to convert to yodish. Yes.`) + }; + message.channel.startTyping(); + 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)); + 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: [] +}; + + exports.help = { + name: "yoda", + category: "Fun", + description: "Turns any text you input into yodish. Yes.", + usage: "yoda " +}; \ No newline at end of file diff --git a/src/commands/zalgo.js b/src/commands/zalgo.js new file mode 100644 index 0000000..e34d404 --- /dev/null +++ b/src/commands/zalgo.js @@ -0,0 +1,30 @@ +const zalgo = require("to-zalgo") +exports.run = async (client, message, args) => { + if(!args[0]) { + return message.channel.send(`<:error:466995152976871434> You didn't provide any text! Usage: \`${client.commands.get(`zalgo`).help.usage}\``) + }; + + var output = zalgo(args.join(" ")) + + if(output.length > 2000) { + output = output.slice(0, -Math.abs(output.length - 2000)) + }; + + message.channel.send(output) + }; + + exports.conf = { + enabled: false, + guildOnly: false, + aliases: [], + permLevel: "User", + requiredPerms: [] + }; + + exports.help = { + name: "zalgo", + category: "Fun", + description: "Spoilers every letter in the provided text.", + usage: "zalgo [text]" + }; + \ No newline at end of file diff --git a/src/events/message.js b/src/events/message.js index 3505c1c..465ce3f 100644 --- a/src/events/message.js +++ b/src/events/message.js @@ -1,4 +1,4 @@ -const commandRanRecently = new Set(); +const cooldown = new Set(); module.exports = async (client, message) => { if (message.author.bot) return; @@ -131,11 +131,17 @@ module.exports = async (client, message) => { }; }; - const prefixMention = new RegExp(`^<@!?${client.user.id}>( |)$`); - - if (message.content.match(prefixMention)) { - return message.channel.send(`Current prefix: \`${prefix}\``); - } + //const prefixMention = new RegExp(`^<@!?${client.user.id}>( |)$`); + const myMention = `<@&${client.user.id}>`; + const myMention2 = `<@!${client.user.id}>`; + + if (message.content.startsWith(myMention) || message.content.startsWith(myMention2)) { + if(message.content.length > myMention.length + 1 && (message.content.substr(0, myMention.length + 1) == myMention + ' ' || message.content.substr(0, myMention2.length + 1) == myMention2 + ' ')) { + prefix = message.content.substr(0, myMention.length) + ' '; + } else { + return message.channel.send(`Current prefix: \`${prefix}\``); + }; + }; if (message.content.indexOf(prefix) !== 0) return; @@ -145,17 +151,22 @@ module.exports = async (client, message) => { if (!cmd) return; - if (commandRanRecently.has(message.author.id)) { + if (cooldown.has(message.author.id)) { return message.channel.send( - `⏱️ You are being ratelimited. Please try again in 2 seconds.` - ) - .then(m => m.delete(2000)); + `⏱️ You are being ratelimited. Please try again in 2 seconds.` + ).then(msg => { + msg.delete({timeout: 2000}); + }); }; - if (!perms.has('SEND_MESSAGES')) { + if (message.guild && !perms.has('SEND_MESSAGES')) { 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( @@ -216,12 +227,13 @@ module.exports = async (client, message) => { message.flags.push(args.shift().slice(1)); }; - commandRanRecently.add(message.author.id); - setTimeout(() => { - commandRanRecently.delete(message.author.id); - }, {timeout: 2000}); + cooldown.add(message.author.id); - client.logger.cmd(`${client.config.permLevels.find(l => l.level === level).name} ${message.author.username} (${message.author.id}) ran command ${cmd.help.name}`); + setTimeout(() => { + cooldown.delete(message.author.id); + }, 2000); + + client.logger.cmd(`${client.config.permLevels.find(l => l.level === level).name} ran command ${cmd.help.name}`); cmd.run(client, message, args, level); -}; \ No newline at end of file +}; diff --git a/src/events/ready.js b/src/events/ready.js index 4a3ce44..532cd8c 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -6,28 +6,55 @@ module.exports = client => { client.lockActivity = false; - client.logger.log(`Connected to Discord as ${client.user.tag} | v${client.version.number}`, 'ready'); + let guild, channel, channel1; - let channel; - let channel1; + if(client.config.loggingServer.length > 0) { + try { + guild = client.guilds.cache.get(client.config.loggingServer) + } catch(err) { + client.logger.error("Could not find loggingServer server (is the ID valid?):\n" + err); + process.exit(1); + }; - try { channel = client.guilds.cache.get('410990517841690625').channels.cache.get('570963998342643732'); } catch(err) {}; - try { channel1 = client.guilds.cache.get('410990517841690625').channels.cache.get('570963481189154822'); } catch(err) {}; + if(client.config.consoleLogs.length > 0) { + try { + channel1 = guild.channels.cache.get(client.config.consoleLogs) + } catch(err) { + client.logger.error("Could not find consoleLogs channel (is the ID valid?):\n" + err); + process.exit(1); + }; + }; + + if(client.config.startupLogs.length > 0) { + try { + channel = guild.channels.cache.get(client.config.startupLogs) + } catch(err) { + client.logger.error("Could not find startupLogs channel (is the ID valid?):\n" + err); + process.exit(1); + }; + }; + }; if(client.devmode == true) { client.logger.warn("Running in development mode.") prefix = client.config.defaultSettings.devprefix; } else { prefix = client.config.defaultSettings.prefix; - channel.send(`\`${timestamp}\`: Ready event fired! Connected to ${client.users.cache.size} users in ${client.guilds.cache.size} guilds.`); - channel1.send(`\`${timestamp}\`: **Ready event fired**`); - } + if(channel) { + channel.send(`Bot started at \`${timestamp}\``); + }; + }; let randomActivity = activityArray.random(); client.user.setActivity(`${prefix + randomActivity} | v${client.version.number}`, {type: "PLAYING"}); + setInterval(() => { randomActivity = activityArray.random(); - if(client.lockActivity == false) client.user.setActivity(`${prefix + randomActivity} | v${client.version.number}`, {type: "PLAYING"}); + if(client.lockActivity == false) { + client.user.setActivity(`${prefix + randomActivity} | v${client.version.number}`, {type: "PLAYING"}); + }; }, 30000); + + client.logger.log(`Connected to Discord as ${client.user.tag} | v${client.version.number}`, 'ready'); }; 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/Logger.js b/src/modules/Logger.js index 0c8614f..e924961 100644 --- a/src/modules/Logger.js +++ b/src/modules/Logger.js @@ -2,17 +2,25 @@ const chalk = require("chalk"); const moment = require("moment"); exports.log = (content, type = "log") => { - const timestamp = chalk.grey(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`); + const timestamp = `[${moment().format("YYYY-MM-DD HH:mm:ss")}]`; let channel; + + try { + channel = client.guilds.cache.get(client.config.loggingServer).channels.cache.get(client.config.consoleLogs); + } catch(err) {}; - try { channel = client.guilds.cache.get('410990517841690625').channels.cache.get('570963481189154822'); } catch(err) {} + var logToServer = false; + + if(client.devmode === false && channel && client.guilds.cache.get(client.config.loggingServer).available) { + logToServer = true; + }; switch (type) { case "info": { try { - if (client.devmode == false) { - channel.send(`\`${timestamp}\`: ` + content); + if (logToServer == true) { + channel.send(`\`${timestamp}\` \`[${type.toUpperCase()}]\` ` + content); }; } catch(err) {}; return console.log(`${timestamp} ${chalk.cyanBright(`[${type.toUpperCase()}]`)} ${content} `); @@ -20,8 +28,8 @@ exports.log = (content, type = "log") => { case "warn": { try { - if (client.devmode == false) { - channel.send(`\`${timestamp}\`: ` + content); + if (logToServer == true) { + channel.send(`\`${timestamp}\` \`[${type.toUpperCase()}]\` ` + content); }; } catch(err) {}; return console.log(`${timestamp} ${chalk.yellowBright(`[${type.toUpperCase()}]`)} ${content} `); @@ -29,8 +37,8 @@ exports.log = (content, type = "log") => { case "error": { try { - if (client.devmode == false) { - channel.send(`\`${timestamp}\`: ` + content); + if (logToServer == true) { + channel.send(`\`${timestamp}\` \`[${type.toUpperCase()}]\` ` + content); }; } catch(err) {} return console.log(`${timestamp} ${chalk.redBright(`[${type.toUpperCase()}]`)} ${content} `); @@ -38,8 +46,8 @@ exports.log = (content, type = "log") => { case "debug": { try { - if (client.devmode == false) { - channel.send(`\`${timestamp}\`: ` + content); + if (logToServer == true) { + channel.send(`\`${timestamp}\` \`[${type.toUpperCase()}]\` ` + content); }; } catch(err) {}; return console.log(`${timestamp} ${chalk.magentaBright(`[${type.toUpperCase()}]`)} ${content} `); @@ -47,14 +55,19 @@ exports.log = (content, type = "log") => { case "cmd": { try { - if (client.devmode == false) { - channel.send(`\`${timestamp}\` ` + content); + if (logToServer == true) { + channel.send(`\`${timestamp}\` \`[${type.toUpperCase()}]\` ` + content); }; } catch(err) {}; return console.log(`${timestamp} ${chalk.whiteBright(`[${type.toUpperCase()}]`)} ${content}`); }; case "ready": { + try { + if (logToServer == true) { + channel.send(`\`${timestamp}\` \`[${type.toUpperCase()}]\` ` + content); + }; + } catch(err) {}; return console.log(`${timestamp} ${chalk.greenBright (`[${type.toUpperCase()}]`)} ${content}`); }; diff --git a/src/modules/functions.js b/src/modules/functions.js index 85ec48d..6a3a9b7 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 request = require('request'); - module.exports = client => { // Permission level function client.permlevel = message => { @@ -133,172 +128,20 @@ module.exports = client => { return a; }; - // 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) - { - request("https://www.googleapis.com/youtube/v3/search?part=id&type=video&q=" + encodeURIComponent(query) + "&key=" + client.config.ytkey, function(error, response, body) - { - if(error) throw error; - - var json = JSON.parse(body); - if(!json.items) { reject(); return; } - resolve(json.items[0]); - }); - }); - } - - client.music.getGuild = function(id) - { - if(client.music.guilds[id]) return client.music.guilds[id]; + // USER OBJECT FROM MENTION + client.getUserFromMention = mention => { + if (!mention) return; - return client.music.guilds[id] = - { - queue: [], - playing: false, - paused: false, - dispatcher: null, - skippers: [] + if (mention.startsWith('<@') && mention.endsWith('>')) { + mention = mention.slice(2, -1); + + if (mention.startsWith('!')) { + mention = mention.slice(1); } - } - 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 = ""; + return client.users.cache.get(mention); } - - var time = hrs + min + ":" + sec; - return time; - }; + } //FIND ROLE client.findRole = function(input, message) { @@ -324,6 +167,12 @@ module.exports = client => { }; }; + // FIND RANDOM INT BETWEEN TWO INTEGERS + client.intBetween = function(min, max){ + return Math.round((Math.random() * (max - min))+min); + }; + + // .toPropercase() returns a proper-cased string Object.defineProperty(String.prototype, "toProperCase", { value: function() { @@ -352,6 +201,6 @@ module.exports = client => { }); 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..a3bb99e --- /dev/null +++ b/src/modules/music.js @@ -0,0 +1,246 @@ +// Copyright 2020 Emily J. / mudkipscience and contributors. Subject to the AGPLv3 license. + +const ytdl = require('ytdl-core-discord') +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 = [] + + 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}v1/videos/${id}`) + } catch (err) { + res = await fetch(`${client.config.endpoints.invidious}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 = [] + // 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(await ytdl(exports.getLinkFromID(v.video.videoId), { highWaterMark: 1024 * 1024 * 32 }), { type: 'opus' }) + } catch (err) { + if (playNext && playNext === true) { + guild.queue.splice(1, 1) + } else { + guild.queue.pop() + } + + client.logger.error(err) + return message.channel.send(`<:error:466995152976871434> An error has occured! If this issue persists, please contact my developers with this:\n\`${err}\``) + } + 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.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 = [] + + connection.disconnect() + } + }) + } + } else { + return message.channel.channelsend('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) + } +} diff --git a/version.json b/version.json index edc38f2..67e2097 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "number": "1.1.1", - "changelog": "**1.1.0 CHANGELOG:**\n> • Added `~softban`, bans and unbans a user to clear messages\n> • Added `~emoji`, enlarges custom emojis\n> • Added `~inspirobot`, generates an inspirational quote\n> • `~serverinfo` has been changed to be more consistent, and also now displays boosts and if the server is partnered and stuff\n> • `~userinfo` has been changed to be more consistent, also added some stuff\n> • `~about` has been changed, added a thumbnail and removed the description\n> • `~colour` has been changed, it can now generate colours from text\n> • `~hackban` no longer has its own embed\n> • `~eval` now logs to hastebin if output is too large\n> • role names are no longer case sensitive\n> • `~echo` renamed `~say`\n> • Users with the ADMINISTRATOR permission now automatically recieve woomy admin\n> • Fixed `~flip`, `~purge`, `~bohemian_rhapsody` and `~creeper`\n> • Guild join/leave messages no longer include the guild name\n> • Some emojis have been changed\n> • Woomy now supports discord.js v12\n> • Files have been restructured\n> • Logger now logs error stack\n> • Restart now exits with code 0" + "number": "1.3.0", + "changelog": "**1.3.0 Changelog:**\n> • Music module has been rewritten for better stability and lots more features\n> • Force disconnecting Woomy from a voice channel no longer breaks music\n> • Music should (hopefully) break less in general\n> • Existing music commands have been rewritten\n> • Added the following new commands: fixmusic, movehere, movesong, playnext, shuffle, songinfo, volume\n> • Updated ship command\n**Notes:**\n> • This will be the final major update to Woomy V1, as we are shifting our focus to Woomy V2, which is a complete rewrite." }