// Load up the discord.js library const Discord = require("discord.js"); // Load up the shell command library var exec = require('child_process').exec; // Define a function to execute a command function execute(command, callback) { exec(command, function(error, stdout, stderr) { callback(stdout); }); }; // This is your client. Some people call it `bot`, some people call it `self`, // some might call it `cootchie`. Either way, when you see `client.something`, or `bot.something`, // this is what we're refering to. Your client. const client = new Discord.Client(); // this allows us to define a voice connection with global scope var connection; var dispatcher; // Here we load the config.json file that contains our token and our prefix values. const config = require("./config.json"); // config.token contains the bot's token // config.prefix contains the message prefix. client.on("ready", () => { // This event will run if the bot starts, and logs in, successfully. console.log(`Bot has started, with ${client.users.size} users, in ${client.channels.size} channels of ${client.guilds.size} guilds.`); // Example of changing the bot's playing game to something useful. `client.user` is what the // docs refer to as the "ClientUser". client.user.setActivity("Taking Over Chicago"); }); client.on("guildCreate", guild => { // This event triggers when the bot joins a guild. console.log(`New guild joined: ${guild.name} (id: ${guild.id}). This guild has ${guild.memberCount} members!`); client.user.setActivity(`Serving ${client.guilds.size} servers`); }); client.on("guildDelete", guild => { // this event triggers when the bot is removed from a guild. console.log(`I have been removed from: ${guild.name} (id: ${guild.id})`); client.user.setActivity("Taking Over Chicago"); }); client.on("message", async message => { // This event will run on every single message received, from any channel or DM. // It's good practice to ignore other bots. This also makes your bot ignore itself // and not get into a spam loop (we call that "botception"). if (message.author.bot) return; // Also good practice to ignore any message that does not start with our prefix, // which is set in the configuration file. if (message.content.indexOf(config.prefix) !== 0) return; // Here we separate our "command" name, and our "arguments" for the command. // e.g. if we have the message "+say Is this the real life?" , we'll get the following: // command = say // args = ["Is", "this", "the", "real", "life?"] const args = message.content.slice(config.prefix.length).trim().split(/ +/g); const command = args.shift().toLowerCase(); // Let's go with a few common example commands! Feel free to delete or change those. if (command === "ping") { // Calculates ping between sending a message and editing it, giving a nice round-trip latency. // The second ping is an average latency between the bot and the websocket server (one-way, not round-trip) const m = await message.channel.send("Ping?"); m.edit(`Pong! Latency is ${m.createdTimestamp - message.createdTimestamp}ms. API Latency is ${Math.round(client.ping)}ms`); } if (command === "say") { // makes the bot say something and delete the message. As an example, it's open to anyone to use. // To get the "message" itself we join the `args` back into a string with spaces: const sayMessage = args.join(" "); // Then we delete the command message (sneaky, right?). The catch just ignores the error with a cute smiley thing. message.delete().catch(O_o=>{}); // And we get the bot to say the thing: message.channel.send(sayMessage); } if (command === "kick") { // This command must be limited to mods and admins. In this example we just hardcode the role names. // Please read on Array.some() returns true if any element meets the passed condition (map-like) if (!message.member.roles.some(r=>["Administrator", "Moderator","Admin","Mod"].includes(r.name)) ) return message.reply("Sorry, you don't have permissions to use this!"); // Let's first check if we have a member and if we can kick them! // message.mentions.members is a collection of people that have been mentioned, as GuildMembers. // We can also support getting the member by ID, which would be args[0] let member = message.mentions.members.first() || message.guild.members.get(args[0]); if (!member) return message.reply("Please mention a valid member of this server"); if (!member.kickable) return message.reply("I cannot kick this user! Do they have a higher role? Do I have kick permissions?"); // slice(1) removes the first part, which here should be the user mention or ID // join(' ') takes all the various parts to make it a single string. let reason = args.slice(1).join(' '); if (!reason) reason = "No reason provided"; // Now, time for a swift kick in the nuts! await member.kick(reason) .catch(error => message.reply(`Sorry ${message.author} I couldn't kick because of : ${error}`)); message.reply(`${member.user.tag} has been kicked by ${message.author.tag} because: ${reason}`); } if (command === "ban") { // Most of this command is identical to kick, except that here we'll only let admins do it. // In the real world mods could ban too, but this is just an example, right? ;) if (!message.member.roles.some(r=>["Administrator"].includes(r.name)) ) return message.reply("Sorry, you don't have permissions to use this!"); let member = message.mentions.members.first(); if (!member) return message.reply("Please mention a valid member of this server"); if (!member.bannable) return message.reply("I cannot ban this user! Do they have a higher role? Do I have ban permissions?"); let reason = args.slice(1).join(' '); if (!reason) reason = "No reason provided"; await member.ban(reason) .catch(error => message.reply(`Sorry ${message.author} I couldn't ban because of : ${error}`)); message.reply(`${member.user.tag} has been banned by ${message.author.tag} because: ${reason}`); } if (command === "join") { // This command moves the bot to the channel given as the first arg. // This is the passed channel const channel = args.join(' '); const channelVar = message.guild.channels.find("name", channel); // Checks if channel is valid. if (!(message.guild.channels.exists("name", channel) && channelVar.type === "voice")) return message.reply(`Sorry ${message.author}, that channel doesn't appear to exist.`); // Joins the channel channelVar.join().then(conn => { connection = conn; console.log('Connected!'); }).catch(console.error); } if (command === "leave") { // this command remove the bot from the channel passed const channel = args.join(' '); const channelVar = message.guild.channels.find("name", channel); if (!(message.guild.channels.exists("name", channel) && channelVar.type === "voice")) return message.reply(`Sorry ${message.author}, that channel doesn't appear to exist.`); channelVar.leave(); } if (command === "play") { // this command plays the song if one song matches the query given in beets const song = args.join(' '); // creates a single string of all args (the song) var path; // this will hold the filepath of our song exec(`beet ls -p title:${song} | wc -l`, function (error, stdout, stderr) { if (error) { return message.reply(`Sorry, I encountered an issue looking for that song: ${error}`); } else if (stdout !== '1\n') { return message.reply(`There were ${stdout} songs that matched your search. Please give the full song name, or wait until I become a less lazy dev.`); } else { exec(`beet ls -p title:${song}`, function (error, stdout, stderr) { if (error) { return message.reply(`Sorry, I encountered an issue looking for that song: ${error}`); } else { path = stdout.trim(); console.log(song); console.log(path); dispatcher = connection.playFile(path); dispatcher.setVolume(0.75); dispatcher.setBitrate(2048); } }); } }); } }); client.login(config.token);