├── Handlers ├── CommandHandler.js └── EventHandler.js ├── LICENSE ├── Procfile ├── README.md ├── _config.yml ├── app.json ├── bot.js ├── colours.json ├── commands ├── AI │ ├── analyze.js │ └── tts.js ├── General │ ├── help.js │ ├── image.js │ ├── join.js │ ├── leave.js │ ├── ping.js │ ├── prune.js │ └── server.js ├── Moderation │ ├── addRole.js │ ├── ban.js │ └── kick.js └── Music │ ├── addplaylist.js │ ├── bassboost.js │ ├── lyrics.js │ ├── next.js │ ├── np.js │ ├── pause.js │ ├── play.js │ ├── playlists.json │ ├── queue.js │ ├── resume.js │ ├── shuffle.js │ ├── skip.js │ ├── stop.js │ └── volume.js ├── events ├── guildMemberAdd.js ├── message.js └── ready.js ├── images ├── logo.png └── wallpaper.jpeg ├── myjsonfile.json ├── package-lock.json ├── package.json └── struct └── Client.js /Handlers/CommandHandler.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | 4 | 5 | exports.run = async (client) => { 6 | 7 | const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); 8 | 9 | //Stores all the command js files into a map of the commands under the client object 10 | for (const file of commandFiles) { 11 | const command = require(`./commands/${file}`); 12 | client.commands.set(command.name, command); 13 | } 14 | async function load_command_from_directory(command_category) { 15 | fs.readdir(`./commands/${command_category}`, (err, files) => { 16 | if (err) return console.error(err); 17 | files.forEach(file => { 18 | if (!file.endsWith('.js')) return; 19 | let subCommands= require(`../commands/${command_category}/${file}`); 20 | //console.log(`ATTEMPTING TO LOAD: ${adminCommands.name} `); 21 | client.commands.set(subCommands.name, subCommands); 22 | console.log(`Successfully Loaded: ${subCommands.name} | Command`) 23 | }); 24 | }); 25 | } 26 | //Dict storing command categories as defined in the sub-directories of the commands folder 27 | var command_categories = { 28 | music: "Music", 29 | moderation: "Moderation", 30 | general: "General", 31 | ai: "AI" 32 | } 33 | 34 | //Iterates through 35 | for (var command in command_categories) { 36 | var value = command_categories[command]; 37 | load_command_from_directory(value) 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Handlers/EventHandler.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | exports.run = (client) => { 4 | // load events 5 | fs.readdir("./events", (err, files) => { // read the events folter 6 | if (err) return console.error(err) 7 | files.forEach(file => { // for each js file, require it 8 | if (!file.endsWith(".js")) return; 9 | const event = require(`../events/${file}`); 10 | let eventName = file.split(".")[0]; 11 | client.on(eventName, event.bind(null, client)); 12 | console.log(`Successfully loaded: ${eventName} | Event`) 13 | }); 14 | }); 15 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Vignesh Duraisamy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: npm start 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Proximity 2 | ##### A Discord Bot 3 | A multipurpose Discord with Image search, AI Image recognition and a music player, designed to be hosted on Heroku. 4 | 5 | Right now, the bot can: 6 | * Play Songs 7 | * Search Images 8 | * Convert Text to Speech 9 | * Recognize & Classify Images (requires an IBM Cloud account to use IBM Watson) 10 | 11 | #### Environment variables (Config Vars) to configure in Heroku: 12 | * prefix = the command prefix for bot (usually it is !) [Example: ! or ~]. 13 | * token = Your unique Discord API Token from the bot's account. 14 | * watson_key = Your unique IBM API key from IBM Cloud. 15 | * watson_url = The URL in IBM Cloud with the API Key. 16 | * giphykey = Your unique Giphy API key. 17 | * youtubeKey = Your unique YouTube API key. 18 | * ownerid = Your Discord ID (the one made up entirely of numbers). 19 | * GENIUS_KEY = Your unique Genius API key. 20 | 21 | #### Heroku Buildpacks 22 | * FFMpeg Buildback https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git 23 | * Official Nodejs Buildpack heroku/nodejs 24 | 25 | #### Commands: 26 | * \play \ : Plays song from YouTube 27 | * \analyze + image attachment: When added to an image caption, triggers the Image Recognitnion. 28 | * \stop : Stops playback. 29 | * \skip : Skips to next song in queue. 30 | * \image \ : Displays images related to the search term. 31 | * \bruh : Displays a bruh gif. 32 | * \tts \ \: Converts text in any script to audio. 33 | * \gif \ : Displays a gif related to the search term. 34 | * \lyrics \ : Searches and displays lyrics from Genius.com. 35 | * \kick \ : Kicks member. 36 | * \ban \ : Bans member. 37 | 38 | ** Language accents: en, it, jp, ru, etc.** 39 | ## To deploy a version of this bot to your Heroku account: 40 | 41 | It's better to do it in order. 42 | 43 | ### Stuff to do in Discord: 44 | * Get a Discord account (Because, Duh!) 45 | * Create an application and profile for your bot and add it to your server. (Look it up on Youtube if you find it tough) 46 | * Give these permissions to your bot: Connect, Speak, Send Messages and Manage Messages. 47 | * Do all the authorization stuff. 48 | * Keep a copy of your bot's token (privately). 49 | 50 | ### Stuff to do in IBM Cloud: 51 | * Create an account 52 | * Create a Visual Recognition resource (the Lite free plan is enough). 53 | * Find the The Api Key and Url in the resource details and keep a copy of it (again, privately), 54 | 55 | ### Stuff to do in Heroku: 56 | * Fork this repo and deploy to Heroku. 57 | * You must see your bot should be building in the application logs or in the overview. If not, then manually deploy it. 58 | * Start the worker dyno "npm start" in the overview. The "web" dyno switches off randomly so its better to keep it off. 59 | ### Pro Tips: 60 | * Keep an eye on the application logs in Heroku to find out any problems. 61 | * Heroku gives only 450 free hours a month, simply adding credit card details will will give an extra 550 hours. 62 | * The Visual Recognition feature only works for images uploaded by users in the server, it does not work on links. 63 | * The IBM Visual Recognition service deletes itself if inactive for a month. 64 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "A Discord Bot", 3 | "description": "A bot with some cool stuff.", 4 | "repository": "https://github.com/vigneshd332/bot-discord-random", 5 | "logo": "https://i.imgur.com/Tqnk48j.png", 6 | "env": { 7 | "PREFIX": { 8 | "description": "Your bot's command prefix.", 9 | "value": "$", 10 | "required": true 11 | }, 12 | "DISCORD_TOKEN": { 13 | "description": "Your bot's bot token.", 14 | "required": true 15 | }, 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bot.js: -------------------------------------------------------------------------------- 1 | 2 | //PACKAGES 3 | const Discord = require('discord.js'); 4 | const Canvas = require('canvas'); 5 | const fs = require('fs'); 6 | const path = require('path') 7 | 8 | //Fetches secret info from the environment variable 9 | require('dotenv-flow').config(); 10 | 11 | //Global Variables 12 | const Client = require('./struct/Client'); 13 | const client = new Client({ token: process.env.token, prefix: process.env.prefix, youtubeKey: process.env.youtubeKey , ownerID: process.env.ownerid}); 14 | 15 | //Initiates the Command and Event Handlers 16 | fs.readdir('./Handlers/', (err, files) => { 17 | if (err) return console.error(err) 18 | 19 | files.forEach(file => { 20 | 21 | const event = require(path.join(__dirname, "Handlers", file)); 22 | event.run(client); 23 | 24 | }) 25 | }); 26 | 27 | //Logs into Client 28 | client.login(client.config.token); 29 | 30 | 31 | exports.client = client; 32 | -------------------------------------------------------------------------------- /colours.json: -------------------------------------------------------------------------------- 1 | { 2 | "purple_dark": "#6a006a", 3 | "purple_medium": "#a958a5", 4 | "purple_light": "#c481fb", 5 | "orange": "#ffa500", 6 | "gold": "#ff5757", 7 | "red_dark": "#8e2430", 8 | "red_light": "#f94343", 9 | "blue_dark": "#3b5998", 10 | "cyan": "#5780cd", 11 | "blue_light": "#ace9e7", 12 | "aqua": "#33a1ee", 13 | "pink": "#ff9dbb", 14 | "green_dark": "#2ac075", 15 | "green_light": "#a1ee33", 16 | "white": "#f9f9f6", 17 | "cream": "#ffdab9" 18 | } 19 | -------------------------------------------------------------------------------- /commands/AI/analyze.js: -------------------------------------------------------------------------------- 1 | const watson = require('watson-developer-cloud'); 2 | 3 | //object containing credentials such as api key and tokens 4 | const credential = { 5 | url: process.env.watson_url, 6 | version: '2018-03-19', 7 | iam_apikey: process.env.watson_key 8 | } 9 | 10 | const ir = new watson.VisualRecognitionV3(credential); 11 | 12 | module.exports = { 13 | name: 'analyze', 14 | description: 'Image recognition powered by IBM Watson', 15 | aliases: ['an'], 16 | execute(msg, args) { 17 | const image = isImage(msg); 18 | image.then( 19 | (value) => { 20 | //checks the type of the promis value. If it is a stirng then run the first block. 21 | if(typeof(value) == "string"){ 22 | console.log("it is a string") 23 | //msg.reply("it is not an image"); 24 | //insert watson conversation here 25 | } 26 | else{ 27 | let replyMessage = getMessage(value); 28 | msg.reply(replyMessage); 29 | } 30 | console.log(value); 31 | }); 32 | 33 | 34 | function isImage(msg) { 35 | try { 36 | //if msg doesnt have a url then this object will not exist and therefore return an error 37 | const image = msg.attachments.array()[0].url; //json object 38 | console.log("image url is " + image); 39 | 40 | //parameters for the general classification 41 | const params_gen = { 42 | url: msg.attachments.array()[0].url, 43 | classifier_ids: "default", 44 | }; 45 | 46 | //paramters for the food classification 47 | const params_food = { 48 | url: msg.attachments.array()[0].url, 49 | classifier_ids: "food" 50 | } 51 | 52 | //first promise that does the first classification 53 | let prom1 = new Promise( 54 | (resolve, reject) => { 55 | //classify object contains image recognition object with general classification 56 | let classify_object = null; 57 | ir.classify(params_gen , (err, res) => { 58 | if(err){ 59 | console.log(err); 60 | reject(err); 61 | } 62 | else{ 63 | classify_object = res; 64 | resolve(classify_object); //first promise resolves a raw classify object 65 | } 66 | }); 67 | } 68 | ); 69 | 70 | 71 | //Function used to check if the image contains food. Takes raw classify object as a parameter 72 | function check_image(image_object){ 73 | for(item of image_object.images[0].classifiers[0].classes){ 74 | if(item.class === 'food' ){ 75 | console.log("It is a food Item") 76 | return true; 77 | } 78 | } 79 | console.log("it is a general item") 80 | return false; 81 | } 82 | 83 | let promise = prom1.then(function(result){ 84 | //check if result contains food or person 85 | let bool = check_image(result); 86 | 87 | //if it is a food image than bool will be false and it will return a new promise 88 | if(bool == true){ 89 | return new Promise((resolve, reject) => { 90 | ir.classify(params_food, 91 | (err ,res) => { 92 | if(err){ 93 | reject(err); 94 | } 95 | else{ 96 | resolve(res); //passes raw classify object 97 | } 98 | }); 99 | }); 100 | } 101 | else{ 102 | return (new Promise( 103 | (resolve, reject) => { 104 | resolve(result); 105 | })); 106 | } 107 | }).catch(err => console.log(err)); 108 | 109 | return(promise); 110 | } 111 | catch (TypeError) { 112 | //returns new promise with value of the text from the message 113 | console.log("Type Error caught") 114 | return (new Promise( 115 | (resolve, reject) => { 116 | resolve(msg.content); 117 | })) 118 | } 119 | } 120 | function getMessage(value){ 121 | let replyMessage = "\nI see... \n"; 122 | let greatestVal = 0; 123 | let answer = null; 124 | for(items of value.images[0].classifiers[0].classes){ 125 | if(items.score > greatestVal){ 126 | greatestVal = item.score; 127 | answer = items.class 128 | } 129 | 130 | let itemClass = items.class; 131 | let percentage = Number(items.score * 100).toFixed(2); //takes the score and converts it to a percentage 132 | 133 | replyMessage = replyMessage + String(itemClass) +" ........ " + percentage + "% \n"; 134 | } 135 | return replyMessage + "I think it is a " + String(answer) ; 136 | } 137 | } 138 | }; 139 | -------------------------------------------------------------------------------- /commands/AI/tts.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | const Discord = require('discord.js'); 3 | const download = util.promisify(require('download-file')); 4 | const tts = require('google-tts-api'); 5 | const usage = new Discord.MessageEmbed() 6 | .setTitle("Invalid Usage!") 7 | .setColor(0xFF0000) 8 | .setDescription(`${process.env.prefix}tts` + " "); 9 | 10 | let awaiting = []; 11 | 12 | 13 | module.exports = { 14 | name: 'tts', 15 | description: 'Text to speech powered by Google. Use '+`${process.env.prefix}tts `+'', 16 | aliases: ['tts'], 17 | execute(message, args) { 18 | awaiting.push(message.author.id); 19 | let lang = message.content.split(" ") 20 | let act = lang[1]; 21 | let toMp3 = message.content.split(" "); 22 | toMp3.shift(); 23 | toMp3.shift(); 24 | toMp3 = toMp3.join(" "); 25 | let member = message.guild.member(message.author); 26 | let nickname = member ? member.displayName : null; 27 | if(!toMp3){message.channel.send(usage); 28 | return; 29 | } 30 | toMp3 = nickname + " says " + toMp3; 31 | 32 | let options = { 33 | directory: `././audio`, 34 | filename: `${message.author.id}.mp3` 35 | } 36 | 37 | if (act === "en" || act === "it" || act == "hi" || act === "ru" || act === "en-US" || act === "en-UK" ){ tts(toMp3, act, 1) 38 | .then(url => { 39 | download(url, options) 40 | .then(() => 41 | message.channel.send({ 42 | files: [{ 43 | attachment: `${options.directory}/${options.filename}`, 44 | name: `${message.author.id}.mp3` 45 | }] 46 | }) 47 | ) 48 | .then(msg => { 49 | //fs.unlink(`${options.directory}/${options.filename}`) 50 | removeAwaiting(message.author.id); 51 | }) 52 | .catch(err => { 53 | console.error(err); 54 | removeAwaiting(message.author.id); 55 | return; 56 | }); 57 | }) 58 | .catch(err => { 59 | message.channel.send(usage); 60 | removeAwaiting(message.author.id); 61 | }); 62 | } 63 | else { 64 | removeAwaiting(message.author.id); 65 | message.channel.send(usage); 66 | } 67 | 68 | function removeAwaiting(id) { 69 | awaiting = awaiting.filter(awaiter => awaiter != id); 70 | } 71 | } 72 | 73 | }; 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /commands/General/help.js: -------------------------------------------------------------------------------- 1 | const parent = require('../../bot.js'); 2 | const prefix = parent.client.config.prefix 3 | const { MessageEmbed } = require('discord.js') 4 | const colours = require('../../colours.json') 5 | module.exports = { 6 | name: 'help', 7 | description: 'List all of my commands or info about a specific command.', 8 | aliases: ['commands'], 9 | usage: '[command name]', 10 | cooldown: 5, 11 | execute(message, args) { 12 | const data = []; 13 | const { commands } = message.client; 14 | 15 | if (!args.length) { 16 | 17 | //GENERAL HELP DM 18 | HEmbed = new MessageEmbed() 19 | .setTitle(`List of Commands`) 20 | .setDescription(`Here is a list of all my commands \nYou can send ${prefix}help [command name] to get info on a specific channel`) 21 | .setColor(colours.gold) 22 | .setThumbnail(parent.client.user.displayAvatarURL()) 23 | .setFooter(`Proximity ${process.env.version}`, parent.client.user.displayAvatarURL()) 24 | commands.forEach((command) => { 25 | HEmbed.addField(`${command.name}`, `${command.description}`, true) 26 | }); 27 | 28 | 29 | 30 | return message.author.send({ split: true, embed: HEmbed }) 31 | .then(() => { 32 | if (message.channel.type === 'dm') return; 33 | message.reply('I\'ve sent you a DM with all my commands!'); 34 | }) 35 | .catch(error => { 36 | console.error(`Could not send help DM to ${message.author.tag}.\n`, error); 37 | message.reply('it seems like I can\'t DM you!'); 38 | }); 39 | } 40 | 41 | 42 | const name = args[0].toLowerCase(); 43 | const command = commands.get(name) || commands.find(c => c.aliases && c.aliases.includes(name)); 44 | 45 | if (!command) { 46 | return message.reply('that\'s not a valid command!'); 47 | } 48 | 49 | HEmbed = new MessageEmbed() 50 | .setTitle(`**Help on ${command.name}**`) 51 | .setThumbnail(message.guild.iconURL()) 52 | .setColor(colours.gold) 53 | .addField(`**Name**`, `${command.name}`, true) 54 | .addField(`**Aliases**`, `${command.aliases.join(', ')}`, true) 55 | .addField(`**Description**`, `${command.description}`, true) 56 | .addField(`**Usage:**`, `${prefix}${command.name} ${command.usage}`, true) 57 | .addField(`**Cooldown:**`, `${command.cooldown || 3} second(s)`, true) 58 | .setFooter(`Proximity ${process.env.version}`, parent.client.user.displayAvatarURL()) 59 | 60 | message.channel.send({ split: true, embed: HEmbed }); 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /commands/General/image.js: -------------------------------------------------------------------------------- 1 | const request = require("request"); 2 | const cheerio = require("cheerio"); 3 | 4 | module.exports = { 5 | name: 'image', 6 | description: 'Image Search Powered by Dogpile', 7 | aliases: ['img'], 8 | execute(message, args) { 9 | var parts = message.content.split(" "); // Splits message into an array for every space, our layout: " [search query]" will become ["", "search query"] 10 | image(message, parts); // Pass requester message to image function 11 | 12 | function image(message, parts) { 13 | const args = message.content.split(' '); 14 | /* extract search query from message */ 15 | 16 | var search = parts.slice(1).join(" "); // Slices of the command part of the array ["!image", "cute", "dog"] ---> ["cute", "dog"] ---> "cute dog" 17 | console.log(search); 18 | var options = { 19 | url: "https://www.dogpile.com/serp?qc=images&q=" + search + "&capv=" + process.env.captcha_key, 20 | method: "GET", 21 | headers: { 22 | "Accept": "text/html", 23 | "User-Agent": "Chrome" 24 | } 25 | }; 26 | request(options, function(error, response, responseBody) { 27 | if (error) { 28 | // handle error 29 | return; 30 | } 31 | 32 | /* Extract image URLs from responseBody using cheerio */ 33 | 34 | $ = cheerio.load(responseBody); // load responseBody into cheerio (jQuery) 35 | 36 | // In this search engine they use ".image a.link" as their css selector for image links 37 | var links = $(".image a.link"); 38 | 39 | // We want to fetch the URLs not the DOM nodes, we do this with jQuery's .attr() function 40 | // this line might be hard to understand but it goes thru all the links (DOM) and stores each url in an array called urls 41 | var urls = new Array(links.length).fill(0).map((v, i) => links.eq(i).attr("href")); 42 | // console.log(urls); 43 | if (!urls.length) { 44 | // Handle no results 45 | return; 46 | } 47 | 48 | // Send result 49 | var size = urls.length; 50 | if(size>5){ 51 | var end = Math.floor(Math.random() * 5) 52 | message.channel.send( urls[end] ); 53 | } 54 | else { 55 | var end2 = Math.floor(Math.random() * 5) 56 | message.channel.send ( urls[end] ); 57 | } 58 | }); 59 | } 60 | 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /commands/General/join.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | var maintenance = false; 3 | module.exports = { 4 | name: 'join', 5 | description: 'Joins the current voice channel', 6 | aliases: ['j'], 7 | execute(message, args) { 8 | if (message.member.voice.channel) { 9 | message.member.voice.channel.join(); 10 | } else { 11 | message.reply("You need to join a channel first") 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /commands/General/leave.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | var maintenance = false; 3 | module.exports = { 4 | name: 'leave', 5 | description: 'Leaves the current voice channel', 6 | execute(message, args) { 7 | if (message.member.voice.channel) { 8 | 9 | 10 | const serverQueue = message.client.queue.get(message.guild.id); 11 | 12 | if (!serverQueue || typeof serverQueue === 'undefined') { 13 | console.log("No queue detected ") 14 | message.member.voice.channel.leave(); 15 | 16 | } else { 17 | 18 | serverQueue.songs = []; 19 | serverQueue.connection.dispatcher.end() 20 | 21 | } 22 | } else { 23 | message.reply("You cannot kick me if you are not in the vc") 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /commands/General/ping.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | var maintenance = false; 3 | module.exports = { 4 | name: 'ping', 5 | description: 'Gets User Ping', 6 | aliases: ['pi'], 7 | execute(message, args) { 8 | message.channel.send("Pinging...") //Placeholder for ping 9 | .then((msg) => { 10 | msg.edit("Ping: " + (Date.now() - msg.createdTimestamp)+" ms") 11 | }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /commands/General/prune.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | var maintenance = false; 3 | module.exports = { 4 | name: 'prune', 5 | description: 'Deletes N most recent messages', 6 | cooldown: 5, 7 | aliases: ['pr', 'del'], 8 | usage: "[Number of Messages]", 9 | execute(message, args) { 10 | const amount = parseInt(args[0]); 11 | 12 | if (isNaN(amount) || (amount < 2 || amount > 100)){ 13 | return message.reply('Please enter a valid integer between 2 and 100'); 14 | } else { 15 | message.channel.bulkDelete(amount, true).catch(err => { 16 | console.error(err); 17 | message.channel.send('There was an error trying to prune messages in this channel!') 18 | }); 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /commands/General/server.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | var maintenance = false; 3 | colours = require('../../colours.json'); 4 | const parent = require('../../bot.js'); 5 | module.exports = { 6 | name: 'server', 7 | description: 'Displays server information', 8 | execute(message, args) { 9 | const sembed = new Discord.MessageEmbed() 10 | .setTitle('Server Information') 11 | .setThumbnail(message.guild.iconURL()) 12 | .setDescription(`Information on ${message.guild.name}`) 13 | .setColor(colours.gold) 14 | .setAuthor(`${message.guild.name} Info`, message.guild.iconURL()) 15 | .addField(`**Guild Name**`, `${message.guild.name}`, true) 16 | .addField(`**Guild Owner**`, `${message.guild.owner}`, true) 17 | .addField(`**Member Count**`, `${message.guild.memberCount}`, true) 18 | .addField(`**Highest Role**`, `${message.guild.roles.highest}`, true) 19 | .addField(`**Region**`, `${message.guild.region}`, true) 20 | .addField(`**Server Creation**`, `${message.guild.createdAt}`, true) 21 | .setFooter(`Proximity | Footer`, parent.client.user.displayAvatarURL()); 22 | message.channel.send(sembed); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /commands/Moderation/addRole.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | var maintenance = false; 3 | const parent = require('../../bot.js') 4 | module.exports = { 5 | name: 'add role', 6 | description: 'Adds a role to user', 7 | aliases: ['addrole'], 8 | async execute(message, args) { 9 | if (message.member.hasPermission('MANAGE_ROLES')) { 10 | var mentionedRole = message.mentions.roles.first(); 11 | var roleMember = message.mentions.members.first(); 12 | if (!mentionedRole) return message.reply(`I am unable to find role: ${mentionedRole}`); 13 | 14 | roleMember.roles.add(mentionedRole).then(message.channel.send("Role successfully added.")).catch(console.error); 15 | } else { 16 | message.reply('You do not have permission to do this') 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /commands/Moderation/ban.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | var maintenance = false; 3 | const parent = require('../../bot.js') 4 | module.exports = { 5 | name: 'ban', 6 | description: 'Bans a user', 7 | aliases: ['ban'], 8 | async execute(message, args) { 9 | if (message.member.hasPermission('BAN_MEMBERS')) { 10 | const member = message.mentions.members.first(); 11 | if (member.id == parent.client.config.botID) { 12 | message.reply("I cannot ban myself") 13 | } else { 14 | 15 | message.guild.members.ban(member); 16 | 17 | } 18 | } else { 19 | message.reply('You do not have permission to ban') 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /commands/Moderation/kick.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | var maintenance = false; 3 | const parent = require('../../bot.js') 4 | module.exports = { 5 | name: 'kick', 6 | description: 'Kicks a certain member', 7 | aliases: ['kick'], 8 | async execute(message, args) { 9 | if (message.member.hasPermission('KICK_MEMBERS')) { 10 | const member = message.mentions.members.first(); 11 | member.kick(); 12 | } else { 13 | message.reply('You do not have permission to kick') 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /commands/Music/addplaylist.js: -------------------------------------------------------------------------------- 1 | const playlistsJSONRaw = require('./playlists.json') 2 | const fs = require('fs') 3 | 4 | module.exports = { 5 | name: 'addplaylist', 6 | description: 'Saves playlist using name and PlaylistURL.', 7 | cooldown: 5, 8 | aliases: ['playlistadd', 'saveplaylist', 'save'], 9 | execute(message, args) { 10 | 11 | 12 | playlistName = args[0] 13 | playlistURL = args[1] 14 | playlistCheck = new RegExp('^https://www.youtube.com/playlist'); 15 | 16 | if (!playlistCheck.test(playlistURL)) return message.channel.send("Please enter a valid playlist URL. Ensure name has no spaces"); 17 | 18 | //Checks for possible duplicates 19 | for (i = 0; i < playlistsJSONRaw.playlists.length; i++) { 20 | 21 | if (playlistsJSONRaw.playlists[i].name == playlistName || playlistsJSONRaw.playlists[i].url == playlistURL) { 22 | return message.channel.send(`ERROR : There is already an existing playlist with that Name/URL`) 23 | } 24 | 25 | } 26 | 27 | //Creates playlist structure 28 | playlist = { 29 | name: playlistName, 30 | url: playlistURL 31 | } 32 | 33 | 34 | console.log(`Playlist Information Gathered as : ${playlist}`) 35 | 36 | 37 | //Defines 38 | 39 | let obj = playlistsJSONRaw 40 | 41 | obj.playlists.push(playlist) 42 | console.log(obj) 43 | var json = JSON.stringify(obj); 44 | 45 | /*fs.writeFile('Commands/Music/playlists.json', json, 'utf8', function callback(err) { 46 | 47 | if (err) console.log(err) 48 | 49 | });*/ 50 | 51 | fs.readFile('Commands/Music/playlists.json', 'utf8', function readFileCallback(err, data) { 52 | if (err) { 53 | console.log(err); 54 | } else { 55 | obj = JSON.parse(data); //now it an object 56 | //console.log(`Playlists Array gathered : ( ${obj.playlists} )`) 57 | obj.playlists.push(playlist); //add some data 58 | //console.log(`Playlists Array updated : ( ${obj.playlists} )`) 59 | json = JSON.stringify(obj); //convert it back to json 60 | //console.log(`Converted to JSON : ( ${json} )`) 61 | fs.writeFile('Commands/Music/playlists.json', json, 'utf8', function callback(err) { 62 | 63 | if(err) console.log(err) 64 | }); // write it back 65 | } 66 | }); 67 | 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /commands/Music/bassboost.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'bass', 3 | description: 'Adjusts player volume.', 4 | cooldown: 5, 5 | aliases: ['volume', 'vol'], 6 | execute(message, args) { 7 | 8 | 9 | if (args) { 10 | 11 | volString = args.join('') 12 | 13 | var volInt = parseInt(volString, 10) 14 | 15 | 16 | const whitelist = ["263467234817605633", "273878953356296194"] 17 | 18 | var isWhitelisted = whitelist.includes(message.author.id) 19 | 20 | if (!isNaN(volInt) && volInt >= 0) { 21 | 22 | if (!isWhitelisted) { 23 | 24 | console.log(`volInt: ${volInt}`) 25 | const { channel } = message.member.voice; 26 | if (!channel) return message.channel.send('I\'m sorry but you need to be in a voice channel to play music!'); 27 | const serverQueue = message.client.queue.get(message.guild.id); 28 | if (!serverQueue) return message.channel.send('There is nothing playing.'); 29 | oldVolume = serverQueue.volume 30 | if (!args[0]) return message.channel.send(`The current volume is: **${serverQueue.volume}**`); 31 | serverQueue.volume = volInt; // eslint-disable-line 32 | serverQueue.connection.dispatcher.setVolumeDecibels(volInt); 33 | return message.channel.send(`Volume has been set from **${oldVolume}** to: **${volInt}**`); 34 | 35 | } else { 36 | 37 | return message.channel.send(" You need to be a Hephaestus Premium member to use this feature. Please contact your Server Admin for more info ") 38 | 39 | } 40 | 41 | 42 | } else { 43 | 44 | return message.channel.send("Volume command only accepts positive/neutral Numerical input") 45 | 46 | } 47 | 48 | } 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /commands/Music/lyrics.js: -------------------------------------------------------------------------------- 1 | 2 | const cheerio = require('cheerio') 3 | const Genius = require('genius-api') 4 | const fetch = require('node-fetch') 5 | const {MessageEmbed} = require('discord.js') 6 | const genius = new Genius(process.env.GENIUS_KEY) 7 | const colours = require('../../colours.json') 8 | 9 | module.exports = { 10 | name: 'lyrics', 11 | description: 'Displays lyrics of searched song.', 12 | cooldown: 5, 13 | aliases: ['lyr'], 14 | usage: 'Video Title & Artist Name' , 15 | execute(message, args) { 16 | 17 | //Function to search the Genius API and grab the first song 18 | function genius_search_song(song_string) { 19 | genius.search(song_string) 20 | .then(function (response) { 21 | 22 | 23 | song = response.hits[0].result 24 | 25 | genius.getSongLyrics(song.url) 26 | 27 | 28 | }) 29 | } 30 | 31 | 32 | //Parses the HTML for the lyrics under the lyrics class 33 | function parseSongHTML(htmlText) { 34 | 35 | try { 36 | 37 | } catch (err) { 38 | console.error(err) 39 | } 40 | const $ = cheerio.load(htmlText) 41 | const lyrics = $(`div[class="lyrics"]`).text().trim() 42 | console.log(lyrics) 43 | console.log(typeof lyrics) 44 | if (!lyrics) return message.channel.send("Could not obtain lyrics. Ensure you have entered the Artist") 45 | if (lyrics.length > 3000) return message.channel.send("Could not provide lyrics as they were over character allowance") 46 | 47 | return message.channel.send(lyrics, {split: true, code: true }) 48 | } 49 | 50 | 51 | //Adds method to Genius class for getting the song lyrics 52 | Genius.prototype.getSongLyrics = function getSongLyrics(geniusUrl) { 53 | return fetch(geniusUrl, { method: 'GET' }) 54 | .then(response => { 55 | if (response.ok) return response.text() 56 | throw new Error('Could not get song URL...') 57 | }) 58 | .then(parseSongHTML) 59 | } 60 | 61 | 62 | if (args.length > 0) { 63 | 64 | searchArgs = args.join(' ') 65 | console.log(`Genius searching for... ${searchArgs}`) 66 | try { 67 | 68 | genius_search_song(searchArgs) 69 | 70 | } catch (err) { 71 | console.error(err) 72 | } 73 | 74 | } else { 75 | 76 | 77 | return message.channel.send("Please supply some arguments") 78 | 79 | } 80 | 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /commands/Music/next.js: -------------------------------------------------------------------------------- 1 | const { Util } = require('discord.js'); 2 | const ytdl = require('ytdl-core'); 3 | const parent = require('../../bot.js') 4 | const youtubeAPI = parent.client.config.youtubeKey 5 | const { google } = require('googleapis'); 6 | const getYoutubePlaylistId = require('get-youtube-playlist-id'); 7 | const Youtube = require('simple-youtube-api'); 8 | const youtube = new Youtube(youtubeAPI); 9 | 10 | module.exports = { 11 | name: 'next', 12 | description: 'Adds a song .', 13 | cooldown: 5, 14 | usage: 'Title/Video URL', 15 | async execute(message, args) { 16 | 17 | urlCheck = new RegExp('^https://www.youtube.com/watch') 18 | playlistCheck = new RegExp('^https://www.youtube.com/playlist') 19 | 20 | req_song = args.join(' ') 21 | 22 | const serverQueue = message.client.queue.get(message.guild.id); 23 | if (!serverQueue) return message.channel.send('There is nothing playing.'); 24 | 25 | 26 | if (playlistCheck.test(req_song)) { 27 | 28 | return message.channel.send("Sorry I cannot add a playlist to next in queue") 29 | 30 | } else { 31 | 32 | if (urlCheck.test(req_song)) { 33 | 34 | console.log("URL") 35 | youtube.getVideo(req_song) 36 | .then(video => { 37 | 38 | const song = { 39 | 40 | title: video.title, 41 | url: video.url, 42 | channel: video.channel.title, 43 | duration: video.duration 44 | 45 | }; 46 | 47 | serverQueue.splice(1, 0, song); 48 | 49 | }) 50 | 51 | .catch(err => { console.log(err) }); 52 | 53 | } else { 54 | 55 | console.log("Title") 56 | 57 | //Searches through the YouTube API for video 58 | 59 | youtube.searchVideos(req_song, 1) 60 | .then(results => { 61 | 62 | result = results[0]; 63 | 64 | const song = { 65 | title: result.title, 66 | url: result.url, 67 | channel: result.channel.title, 68 | } 69 | 70 | console.log(`SONG FOUND: ${song.title} by ${song.channel}`) 71 | 72 | try { 73 | serverQueue.songs.splice(1, 0, song) 74 | message.channel.send(`${song.title} has been added next in queue`) 75 | .then((msg) => { 76 | msg.delete({ timeout: 5000 }) 77 | }) 78 | } catch { 79 | message.channel.send("There was an error when adding this song") 80 | } 81 | 82 | }) 83 | .catch(err => { 84 | console.log(err) 85 | }); 86 | } 87 | 88 | 89 | } 90 | 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /commands/Music/np.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'np', 3 | description: 'Shows current song.', 4 | cooldown: 5, 5 | execute(message) { 6 | const serverQueue = message.client.queue.get(message.guild.id); 7 | if (!serverQueue) return message.channel.send('There is nothing playing.'); 8 | return message.channel.send(`🎶 Now playing: **${serverQueue.songs[0].title}** by **${serverQueue.songs[0].channel}**`); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /commands/Music/pause.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'pause', 3 | description: 'Pauses player.', 4 | cooldown: 5, 5 | execute(message) { 6 | const serverQueue = message.client.queue.get(message.guild.id); 7 | if (serverQueue && serverQueue.playing) { 8 | serverQueue.playing = false; 9 | serverQueue.connection.dispatcher.pause(); 10 | return message.channel.send('⏸ Paused the music for you!'); 11 | } 12 | return message.channel.send('There is nothing playing.'); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /commands/Music/play.js: -------------------------------------------------------------------------------- 1 | const { Util } = require('discord.js'); 2 | const ytdl = require('ytdl-core'); 3 | const parent = require('../../bot.js') 4 | const youtubeAPI = parent.client.config.youtubeKey 5 | const { google } = require('googleapis'); 6 | const getYoutubePlaylistId = require('get-youtube-playlist-id'); 7 | const Youtube = require('simple-youtube-api'); 8 | const youtube = new Youtube(youtubeAPI); 9 | const savedPlaylistJSON = require('./playlists.json') 10 | 11 | 12 | module.exports = { 13 | name: 'play', 14 | description: 'Plays / Queues a new song', 15 | usage: '[Video URL | Song Name | PlaylistURL]', 16 | args: true, 17 | cooldown: 3, 18 | aliases: ['p'], 19 | async execute(message, args) { 20 | 21 | 22 | //Permissions and checks 23 | const { channel } = message.member.voice; 24 | if (!channel) return message.channel.send('I\'m sorry but you need to be in a voice channel to play music!'); 25 | const permissions = channel.permissionsFor(message.client.user); 26 | if (!permissions.has('CONNECT')) return message.channel.send('I cannot connect to your voice channel, make sure I have the proper permissions!'); 27 | if (!permissions.has('SPEAK')) return message.channel.send('I cannot speak in this voice channel, make sure I have the proper permissions!'); 28 | 29 | //Defines the requested song based on the args 30 | req_song = args.join(" "); 31 | 32 | //Regular Expressions to check URL 33 | urlCheck = new RegExp('^https://www.youtube.com/watch') 34 | playlistCheck = new RegExp('^https://www.youtube.com/playlist') 35 | 36 | 37 | async function playlist_scan(id) { 38 | 39 | //Gets playlist information based off the URL 40 | youtube.getPlaylist(id) 41 | .then(playlist => { 42 | playlist.getVideos() 43 | .then(videos => { 44 | videos.forEach((video) => { 45 | const song = { 46 | title: video.title, 47 | url: video.url, 48 | channel: video.channel.title, 49 | duration: video.duration 50 | }; 51 | try { 52 | play(song, true) 53 | } catch (err) { 54 | console.log(err) 55 | } 56 | }) 57 | }) 58 | }) 59 | } 60 | 61 | async function play(song, isPlaylist) { 62 | 63 | 64 | //Creates server queue 65 | const serverQueue = message.client.queue.get(message.guild.id); 66 | 67 | //If a queue exists then add the song to the end of the queue 68 | if (serverQueue) { 69 | serverQueue.songs.push(song); 70 | if(!isPlaylist) message.channel.send(`** ${song.title} ** has been added to the queue`) 71 | console.log(serverQueue.songs); 72 | return 73 | } 74 | 75 | //Defines the structure for a queue 76 | const queueConstruct = { 77 | textChannel: message.channel, 78 | voiceChannel: channel, 79 | connection: null, 80 | songs: [], 81 | volume: 4, 82 | playing: true 83 | }; 84 | 85 | //Sets queue variable within the client class 86 | message.client.queue.set(message.guild.id, queueConstruct); 87 | queueConstruct.songs.push(song); 88 | if (isPlaylist) { 89 | message.channel.send("** Playlist ** has been added to the queue") 90 | } else { 91 | message.channel.send(`** ${song.title} ** has been added to the queue`) 92 | } 93 | 94 | //Play 95 | const play = async song => { 96 | const queue = message.client.queue.get(message.guild.id); 97 | if (!song) { 98 | queue.voiceChannel.leave() 99 | message.client.queue.delete(message.guild.id); 100 | return; 101 | } 102 | 103 | //Streams the song 104 | const dispatcher = queue.connection.play(ytdl(song.url)) 105 | 106 | //Once completed it moves the queue array 107 | .on('finish', () => { 108 | queue.songs.shift(); 109 | play(queue.songs[0]); 110 | 111 | }) 112 | 113 | 114 | .on('disconnect', () => { 115 | 116 | console.log("Disconnecting...") 117 | queue.songs = []; 118 | dispatcher.end('Stop command has been used!'); 119 | 120 | }) 121 | 122 | 123 | parent.client.on('voiceStateUpdate', async newState => { 124 | 125 | try { 126 | 127 | if (!newState.connection && newState.member.user.bot) { 128 | console.log("Not connected to a voice channel") 129 | 130 | var serverQueue = message.client.queue.get(message.guild.id) 131 | if (!serverQueue) return console.log("No songs in Queue") 132 | 133 | serverQueue.songs = [] 134 | dispatcher.end(); 135 | console.log("Queue has been cleared because I got disconnected from the voiceChannel") 136 | } else { 137 | 138 | //console.log(`VOICE STATE UPDATED: ${newState.connection.status}`) 139 | 140 | } 141 | 142 | } catch (err) { 143 | console.log(err) 144 | } 145 | }) 146 | 147 | //Sets logarithmic volume 148 | dispatcher.setVolumeLogarithmic(queue.volume / 5); 149 | message.channel.send(`🎶 Start playing: **${song.title}** by **${song.channel}**`) 150 | .then(msg => { 151 | msg.delete({ timeout: 15000 }) 152 | }).catch(err => { console.log(err) }); 153 | }; 154 | 155 | //Instantiates the queue using the queueConstruct definitons and begins to play songs in the queue 156 | try { 157 | const connection = await channel.join(); 158 | queueConstruct.connection = connection; 159 | play(queueConstruct.songs[0]); 160 | 161 | } catch (error) { 162 | console.error(`I could not join the voice channel: ${error}`); 163 | message.client.queue.delete(message.guild.id); 164 | await channel.leave(); 165 | return message.channel.send(`I could not join the voice channel: ${error}`); 166 | } 167 | 168 | 169 | 170 | 171 | }; 172 | 173 | async function searchYoutube(song_string) { 174 | 175 | if (urlCheck.test(song_string)) { 176 | youtube.getVideo(song_string) 177 | .then(video => { 178 | 179 | const song = { 180 | 181 | title: video.title, 182 | url: video.url, 183 | channel: video.channel.title, 184 | duration: video.duration 185 | 186 | }; 187 | 188 | try { 189 | play(song,false) 190 | } catch (err) { 191 | console.log(err) 192 | } 193 | 194 | }) 195 | 196 | .catch(err => { console.log(err) }); 197 | 198 | } else { 199 | 200 | //Searches through the YouTube API for video 201 | 202 | youtube.searchVideos(req_song, 1) 203 | .then(results => { 204 | 205 | result = results[0]; 206 | 207 | const song = { 208 | title: result.title, 209 | url: result.url, 210 | channel: result.channel.title, 211 | } 212 | 213 | play(song,false) 214 | 215 | }) 216 | .catch(err => { 217 | console.log(err) 218 | }); 219 | } 220 | } 221 | 222 | function playlist(playlist) { 223 | var id = getYoutubePlaylistId(playlist) 224 | console.log(`URL : ${playlist} , ID : ${id}`) 225 | playlist_scan(id); 226 | } 227 | 228 | if (playlistCheck.test(req_song)) { 229 | 230 | playlist_scan(req_song) 231 | 232 | } else { 233 | 234 | for (i = 0; i < savedPlaylistJSON.playlists.length; i++) { 235 | if (savedPlaylistJSON.playlists[i].name == req_song) { 236 | 237 | console.log("ARGUMENTS FOUND IN SAVED PLAYLIST NAME... INIATING PLAYLIST") 238 | return playlist_scan(savedPlaylistJSON.playlists[i].url) 239 | } 240 | } 241 | 242 | searchYoutube(req_song) 243 | 244 | } 245 | 246 | } 247 | }; 248 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /commands/Music/playlists.json: -------------------------------------------------------------------------------- 1 | {"playlists":[{"name":"bops","url":"https://www.youtube.com/playlist?list=PL98hZj_nOJFgJmFf5iPCXk43fwmT9FEwt"},{"name":"LemonySnicket","url":"https://www.youtube.com/playlist?list=PL98hZj_nOJFgHqhZ1lREG6S293xRIuLVd"},{"name":"BestOfSongs","url":"https://www.youtube.com/playlist?list=PLxjNo7RqY8MbR2a1ZQjSPSZtG8oJAFWMd"}]} -------------------------------------------------------------------------------- /commands/Music/queue.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require('discord.js'); 2 | const colours = require('../../colours.json'); 3 | const parent = require('../../bot.js') 4 | module.exports = { 5 | name: 'queue', 6 | description: 'Displays current queue.', 7 | cooldown: 3, 8 | aliases: ['q'], 9 | execute(message) { 10 | currentGuild = message.guild 11 | 12 | function queue_to_text(queue) { 13 | queueSize = queue.songs.length 14 | 15 | if (queueSize > 10) { 16 | max_loop = 11 17 | songsLeft = queueSize - 10 18 | numberUpcoming = 10 19 | } else { 20 | max_loop = queueSize 21 | songsLeft = queueSize - 1 22 | numberUpcoming = queueSize 23 | } 24 | 25 | console.log(queue.songs) 26 | 27 | data = new MessageEmbed({type: "rich"}) 28 | .setTitle(`Music Queue`) 29 | .setColor(colours.gold) 30 | .setDescription(`These are the next songs of the playlist`) 31 | .setThumbnail(currentGuild.iconURL()) 32 | .addField(`**NOW PLAYING**`, `${queue.songs[0].title} by ${queue.songs[0].channel}\n---------------------------------------------------`) 33 | .setFooter(`Proximity | ${songsLeft} songs left`, parent.client.user.displayAvatarURL()) 34 | 35 | for (i = 1; i < max_loop; i++) { 36 | currentSong = queue.songs[i] 37 | console.log(currentSong) 38 | data.addField(`#${i} `, `Song Title: **${currentSong.title}** by **${currentSong.channel}**`) 39 | } 40 | return data 41 | } 42 | 43 | const serverQueue = message.client.queue.get(message.guild.id); 44 | 45 | console.log(!serverQueue) 46 | 47 | if (!serverQueue) return message.channel.send('There is nothing playing.'); 48 | queue_embed= queue_to_text(serverQueue) 49 | message.channel.send(queue_embed) 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /commands/Music/resume.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'resume', 3 | description: 'Resumes player.', 4 | cooldown: 5, 5 | execute(message) { 6 | const serverQueue = message.client.queue.get(message.guild.id); 7 | if (serverQueue && !serverQueue.playing) { 8 | serverQueue.playing = true; 9 | serverQueue.connection.dispatcher.resume(); 10 | return message.channel.send('▶ Resumed the music for you!'); 11 | } 12 | return message.channel.send('There is nothing playing.'); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /commands/Music/shuffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'shuffle', 3 | description: 'Shuffles Queue.', 4 | cooldown: 3, 5 | execute(message) { 6 | 7 | //Works off the Fisher Yates Algorithm 8 | function shuffle(queue) { 9 | 10 | for (let i = queue.songs.length - 1; i > 0; i--) { 11 | const j = Math.floor(Math.random() * i) 12 | const temp = queue.songs[i] 13 | queue.songs[i] = queue.songs[j] 14 | queue.songs[j] = temp 15 | } 16 | 17 | } 18 | 19 | //Gets current queue 20 | serverQueue = message.client.queue.get(message.guild.id) 21 | 22 | //Checks Queue 23 | if (!serverQueue.songs[0]) return message.channel.send("No song currently playing in this guild") 24 | 25 | //Grabs Voice Channel 26 | const { channel } = message.member.voice 27 | 28 | //Checks channel 29 | if (!channel) return message.channel.send("You need to be in a voice channel to shuffle music") 30 | 31 | //Shuffles Queue and moves to next song 32 | shuffle(serverQueue); 33 | serverQueue.connection.dispatcher.end() 34 | return message.channel.send("The Queue is now shuffled") 35 | } 36 | }; -------------------------------------------------------------------------------- /commands/Music/skip.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'skip', 3 | description: 'Skip next N Songs.', 4 | cooldown: 5, 5 | execute(message, args) { 6 | 7 | let toSkip = 1 // Default 1 8 | indexString = args[0] 9 | var index_to_skip = parseInt(indexString, 10) 10 | 11 | console.log(`Argument Type: ${typeof index_to_skip}`) 12 | if (typeof index_to_skip !== "number") { 13 | return message.channel.send("Please enter only Integer values") 14 | } else { 15 | const { channel } = message.member.voice; 16 | if (!channel) return message.channel.send('I\'m sorry but you need to be in a voice channel to play music!'); 17 | const serverQueue = message.client.queue.get(message.guild.id); 18 | 19 | var queueSize = serverQueue.songs.length 20 | if (index_to_skip > queueSize) { 21 | console.log("ERROR: User attempted to skip more than queue length") 22 | message.channel.send(`Error: Cannot skip more than the current queue length of ${queueSize}`) 23 | } else { 24 | if (!serverQueue) return message.channel.send('There is nothing playing that I could skip for you.'); 25 | 26 | serverQueue.songs.splice(0, index_to_skip) 27 | serverQueue.connection.dispatcher.end() 28 | 29 | } 30 | 31 | } 32 | 33 | } 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /commands/Music/stop.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'stop', 3 | description: 'Stops Music', 4 | cooldown: 5, 5 | execute(message) { 6 | const { channel } = message.member.voice; 7 | if (!channel) return message.channel.send('I\'m sorry but you need to be in a voice channel to play music!'); 8 | const serverQueue = message.client.queue.get(message.guild.id); 9 | if (!serverQueue) return message.channel.send('There is nothing playing that I could stop for you.'); 10 | serverQueue.songs = []; 11 | serverQueue.connection.dispatcher.end() 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /commands/Music/volume.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'volume', 3 | description: 'Adjusts player volume.', 4 | cooldown: 5, 5 | aliases: ['volume', 'vol'], 6 | execute(message, args) { 7 | 8 | 9 | if (args) { 10 | 11 | volString = args.join('') 12 | 13 | var volInt = parseInt(volString, 10) 14 | 15 | 16 | const whitelist = ["287623568609640451", "371309967342436356", "273878953356296194", "161587365171822592"] 17 | 18 | var isWhitelisted = whitelist.includes(message.author.id) 19 | 20 | if (!isNaN(volInt) && volInt >= 5) { 21 | 22 | if (!isWhitelisted) { 23 | 24 | if (volInt > 999) return message.channel.send("Volume cannot be set above 999") 25 | console.log(`volInt: ${volInt}`) 26 | const { channel } = message.member.voice; 27 | if (!channel) return message.channel.send('I\'m sorry but you need to be in a voice channel to play music!'); 28 | const serverQueue = message.client.queue.get(message.guild.id); 29 | if (!serverQueue) return message.channel.send('There is nothing playing.'); 30 | oldVolume = serverQueue.volume 31 | if (!args[0]) return message.channel.send(`The current volume is: **${serverQueue.volume}**`); 32 | serverQueue.volume = volInt; // eslint-disable-line 33 | serverQueue.connection.dispatcher.setVolumeLogarithmic(volInt / 5); 34 | return message.channel.send(`Volume has been set from **${oldVolume}** to: **${volInt}**`); 35 | 36 | } else { 37 | 38 | return message.channel.send(" You need to be a Hephaestus Premium member to use this feature. Please contact your Server Admin for more info ") 39 | 40 | } 41 | 42 | 43 | } else { 44 | 45 | return message.channel.send("Volume command only accepts positive/neutral Numerical input...Also fuck you matthew") 46 | 47 | } 48 | 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /events/guildMemberAdd.js: -------------------------------------------------------------------------------- 1 | const Canvas = require('canvas') 2 | const Discord = require('discord.js') 3 | 4 | module.exports = async (client, message, member) => { 5 | 6 | 7 | const applyText = (canvas, text) => { 8 | const ctx = canvas.getContext('2d'); 9 | let fontSize = 70; 10 | 11 | do { 12 | ctx.font = `${fontSize -= 10}px sans-serif`; 13 | } while (ctx.measureText(text).width > canvas.width - 300); 14 | 15 | return ctx.font; 16 | } 17 | 18 | console.log('Member Added'); 19 | const channel = member.guild.channels.cache.find(ch => ch.name === 'welcome'); 20 | if (!channel) return console.log('Bot failed to find a "Welcome Channel". Please ensure a welcome channel is set'); 21 | 22 | 23 | const canvas = Canvas.createCanvas(700, 250); 24 | const ctx = canvas.getContext('2d'); 25 | 26 | 27 | const background = await Canvas.loadImage('images/wallpaper.jpeg'); 28 | ctx.drawImage(background, 0, 0, canvas.width, canvas.height); 29 | 30 | 31 | ctx.strokeStyle = '#74037b'; 32 | ctx.strokeRect(0, 0, canvas.width, canvas.height); 33 | 34 | 35 | ctx.font = '28px sans-serif'; 36 | ctx.fillStyle = '#ffffff'; 37 | ctx.fillText('Welcome to the server,', canvas.width / 2.5, canvas.height / 3.5); 38 | 39 | 40 | ctx.font = applyText(canvas, `${member.displayName}!`); 41 | ctx.fillStyle = '#ffffff'; 42 | ctx.fillText(`${member.displayName}!`, canvas.width / 2.5, canvas.height / 1.8); 43 | 44 | 45 | ctx.beginPath(); 46 | ctx.arc(125, 125, 100, 0, Math.PI * 2, true); 47 | ctx.closePath(); 48 | ctx.clip(); 49 | 50 | 51 | const avatar = await Canvas.loadImage(member.user.displayAvatarURL({ format: 'jpg' })); 52 | ctx.drawImage(avatar, 25, 25, 200, 200); 53 | 54 | 55 | const attachment = new Discord.MessageAttachment(canvas.toBuffer(), 'welcome-image.png'); 56 | 57 | 58 | channel.send(`Welcome to the server, ${member}!`, attachment); 59 | 60 | 61 | let role = message.guild.roles.find(r => r.name === "Private Channels"); 62 | console.log(`Role to be given: ${role}`); 63 | 64 | member.roles.add(role).catch(console.error); 65 | 66 | } 67 | -------------------------------------------------------------------------------- /events/message.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js') 2 | const { MessageEmbed } = require('discord.js'); 3 | let embed = new MessageEmbed(); 4 | module.exports = async (client, message) => { 5 | 6 | const cooldowns = new Discord.Collection(); 7 | 8 | //If message doesnt start with prefix or is a bot message 9 | if (!message.content.startsWith(client.config.prefix) || message.author.bot) return; 10 | //Confessions function 11 | if (message.channel.type === 'dm'){ 12 | if (message.author.bot) return; 13 | if (!message.content.startsWith(`${process.env.prefix}confess`)) return; 14 | let conf = message.content.split(" "); 15 | conf.shift(); 16 | conf = conf.join(" "); 17 | 18 | embed = new MessageEmbed(); 19 | embed.setColor(16712480); 20 | embed.setTitle(conf); 21 | embed.setDescription('I read these confessions and send them to Zuckerberg LOL'); 22 | const conchannel = client.channels.cache.find(channel => channel.id === process.env.confess_channel) 23 | conchannel.send(embed) 24 | } 25 | //Splits args and command 26 | const args = message.content.slice(client.config.prefix.length).split(' '); 27 | const commandName = args.shift().toLowerCase(); 28 | 29 | //Stores command from the commands folder 30 | const command = client.commands.get(commandName) 31 | || client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName)); 32 | 33 | //If command doesnt exist then returns 34 | if (!command) return; 35 | 36 | //Checks if args were entered 37 | if (command.args && !args.length) { 38 | let reply = `You didn't provide any arguments, ${message.author}!`; 39 | 40 | if (command.usage) { 41 | reply += `\nThe proper usage would be: \`${client.config.prefix}${command.name} ${command.usage}\``; 42 | } 43 | 44 | return message.channel.send(reply); 45 | } 46 | 47 | 48 | //Cooldowns 49 | if (!cooldowns.has(command.name)) { 50 | cooldowns.set(command.name, new Discord.Collection()); 51 | } 52 | 53 | 54 | const now = Date.now(); 55 | const timestamps = cooldowns.get(command.name); 56 | const cooldownAmount = (command.cooldown || 30) * 1000; 57 | 58 | if (timestamps.has(message.author.id)) { 59 | const expirationTime = timestamps.get(message.author.id) + cooldownAmount; 60 | 61 | if (now < expirationTime) { 62 | const timeLeft = (expirationTime - now) / 1000; 63 | message.reply(`please wait ${timeLeft.toFixed(1)} more second(s) before reusing the \`${command.name}\` command.`) 64 | .then(msg => { 65 | msg.delete({ timeout: timeLeft.toFixed(1) }) 66 | }) 67 | } 68 | } 69 | 70 | timestamps.set(message.author.id, now); 71 | setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); 72 | 73 | 74 | //Attempts the execution of the command and catches any runtime errors. 75 | try { 76 | command.execute(message, args); 77 | } catch (error) { 78 | console.log(error); 79 | message.reply('there was an error trying to execute that command!'); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /events/ready.js: -------------------------------------------------------------------------------- 1 | module.exports = (client, message) => { 2 | 3 | console.log(`Signed in as: ${client.user.tag}`); 4 | console.log(`client.config.prefix: ${client.config.prefix}`); 5 | 6 | client.user.setStatus('Available'); 7 | client.user.setPresence({ activity: { name: "Some random Netflix show" } }); 8 | try { 9 | client.user.setPresence({ activity: { type: "WATCHING", name: "the console logs", url: "https://vigneshd332.github.io/proximity/" } }); 10 | } catch (err) { 11 | console.log(err) 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vigneshd332/proximity/c8e5ee48f07b7d12e6190922fd74f40bcf580d71/images/logo.png -------------------------------------------------------------------------------- /images/wallpaper.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vigneshd332/proximity/c8e5ee48f07b7d12e6190922fd74f40bcf580d71/images/wallpaper.jpeg -------------------------------------------------------------------------------- /myjsonfile.json: -------------------------------------------------------------------------------- 1 | {"playlists":[{"name":"bops","url":"https://www.youtube.com/playlist124"},{"name":"bops","url":"https://www.youtube.com/playlist124"}]} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proximity", 3 | "version": "1.1.0", 4 | "description": "a simple discord bot using nodejs", 5 | "main": "bot.js", 6 | "engines": { 7 | "node": "12.18.0" 8 | }, 9 | "scripts": { 10 | "test": "npm start", 11 | "start": "node bot.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/vigneshd332/proximity.git" 16 | }, 17 | "keywords": [ 18 | "Bot", 19 | "JavaScript", 20 | "DiscordAPI", 21 | "DiscordJS", 22 | "Music Bot", 23 | "Discord", 24 | "Discord Music Bot", 25 | "Hephaestus Bot", 26 | "Moderation", 27 | "Discord Moderation Bot", 28 | "DiscordJS Documentation" 29 | ], 30 | "author": "Vignesh D", 31 | "license": "ISC", 32 | "bugs": { 33 | "url": "https://github.com/vigneshd332/proximity/issues" 34 | }, 35 | "homepage": "https://github.com/vigneshd332/proximity#readme", 36 | "dependencies": { 37 | "@discordjs/opus": "^0.8.0", 38 | "canvas": "^2.11.2", 39 | "cheerio": "^1.0.0-rc.3", 40 | "discord": "^0.8.2", 41 | "discord-player": "^1.4.1", 42 | "discord-youtube-api": "^0.1.0", 43 | "discord.js": "^12.2.0", 44 | "discord.js-commando": "^0.10.0", 45 | "discord.js-musicbot-addon": "^13.9.1", 46 | "dotenv": "^8.2.0", 47 | "dotenv-flow": "^3.1.0", 48 | "download-file": "^0.1.5", 49 | "fs": "0.0.1-security", 50 | "genius-api": "^0.4.1", 51 | "get-youtube-playlist-id": "0.0.1", 52 | "google-tts-api": "0.0.4", 53 | "googleapis": "^52.0.0", 54 | "heroku-logger": "^0.3.3", 55 | "js": "^0.1.0", 56 | "lodash": "^4.17.21", 57 | "node-fetch": "^3.2.10", 58 | "node-opus": "^0.3.3", 59 | "node-pre-gyp": "^0.14.0", 60 | "puppeteer": "^3.3.0", 61 | "request": "^2.88.0", 62 | "sequelize": "^5.21.11", 63 | "sqlite3": "^5.1.7", 64 | "superagent": "^5.2.2", 65 | "watson-developer-cloud": "^3.8.0", 66 | "youtube-search": "^1.1.6", 67 | "ytdl-core-discord": "^1.2.0" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /struct/Client.js: -------------------------------------------------------------------------------- 1 | const { Client, Collection } = require('discord.js'); 2 | 3 | module.exports = class extends Client { 4 | constructor(config) { 5 | super({ 6 | disableMentions: 'everyone' 7 | }); 8 | 9 | this.commands = new Collection(); 10 | 11 | this.cooldowns = new Collection(); 12 | 13 | this.queue = new Map(); 14 | 15 | this.config = config; 16 | } 17 | }; 18 | --------------------------------------------------------------------------------