diff --git a/discord.js b/discord.js index 59f62ea..81d3908 100755 --- a/discord.js +++ b/discord.js @@ -3,293 +3,344 @@ const { Client } = require("discord.js"), fs = require("fs"), path = require("path"), axios = require("axios"), - moment = require("moment"), - timers = require("./lib/timers.js"), staticCommands = require("./lib/static-commands.js"), - //cooldowns = require('./lib/cooldowns.js'), ankhbotCommands = require("./lib/ankhbot-commands.js"), config = require("./config.json"); -// Set up Discord client -const client = new Client(); +function chunkSubstr(str, size) { + const numChunks = Math.ceil(str.length / size); + const chunks = new Array(numChunks); -// Set up SFX -const sfxFilePath = path.join(__dirname, "sfx"); -const allowedSfxChannels = new RegExp(config.allowedSfxChannels); -let playOptions = { volume: config.sfxVolume, passes: config.passes }; -let playing = false; - -// Read in sfx directory, filenames are the commands -let sfxList = readSfxDirectory(sfxFilePath); -// Watch directory for changes and update the list -fs.watch(sfxFilePath, (eventType, filename) => { - if (eventType === "rename") { - sfxList = readSfxDirectory(sfxFilePath); + for (let i = 0, o = 0; i < numChunks; ++i, o += size) { + chunks[i] = str.substr(o, size); } -}); -// @todo DRY this shit up + return chunks; +} -// Read in fun facts -const funFactsFilePath = path.join(__dirname, "conf", "funfacts"); -let funFacts = parseLines(funFactsFilePath); -fs.watchFile(funFactsFilePath, (curr, prev) => { - if (curr.mtime !== prev.mtime) { - funFacts = parseLines(funFactsFilePath); - } -}); +function init(config) { + // Set up Discord client + const client = new Client(); -// Read in ham facts -const hamFactsFilePath = path.join(__dirname, "conf", "hamfacts"); -let hamFacts = parseLines(hamFactsFilePath); -fs.watchFile(hamFactsFilePath, (curr, prev) => { - if (curr.mtime !== prev.mtime) { - hamFacts = parseLines(hamFactsFilePath); - } -}); + // Set up SFX + const sfxFilePath = path.join(__dirname, "sfx"); + let playing = false; -// Set up the native commands to handle -const commands = { - sfx: (msg, disconnectAfter) => { - if (!allowedSfxChannels.test(msg.channel.name)) return; - let sfx = msg.content.split(" ")[1]; - - // retrieve sfx list from pastebin - if (sfx == "" || sfx === undefined) { - axios - .get("https://pastebin.com/raw/vRsZxrrw") - .then(res => { - return msg.channel.send("```" + res.data + "```"); - }) - .catch(console.error); - - return true; + // Read in sfx directory, filenames are the commands + let sfxList = readSfxDirectory(sfxFilePath); + // Watch directory for changes and update the list + fs.watch(sfxFilePath, (eventType, filename) => { + if (eventType === "rename") { + sfxList = readSfxDirectory(sfxFilePath); } + }); - if (playing === true) - return msg.channel.send("Already playing, please wait."); + // @todo DRY this shit up - // make sure this file exists either as an mp3 or wav - let sfxPath; - if (fs.existsSync(path.join(sfxFilePath, sfx + ".mp3"))) { - sfxPath = path.join(sfxFilePath, sfx + ".mp3"); - } else if (fs.existsSync(path.join(sfxFilePath, sfx + ".wav"))) { - sfxPath = path.join(sfxFilePath, sfx + ".wav"); - } else { - return msg.reply("This sound effect does not exist!"); + // Read in fun facts + const funFactsFilePath = path.join(__dirname, "conf", "funfacts"); + let funFacts = parseLines(funFactsFilePath); + fs.watchFile(funFactsFilePath, (curr, prev) => { + if (curr.mtime !== prev.mtime) { + funFacts = parseLines(funFactsFilePath); } + }); - if (!msg.guild.voiceConnection) - return joinVoiceChannel(msg).then(() => - commands.sfx(msg, disconnectAfter) - ); + // Read in ham facts + const hamFactsFilePath = path.join(__dirname, "conf", "hamfacts"); + let hamFacts = parseLines(hamFactsFilePath); + fs.watchFile(hamFactsFilePath, (curr, prev) => { + if (curr.mtime !== prev.mtime) { + hamFacts = parseLines(hamFactsFilePath); + } + }); - disconnectAfter = - typeof disconnectAfter !== "undefined" ? disconnectAfter : true; + // Set up the native commands to handle + const commands = { + sfx: (msg, guildConfig) => { + let allowedSfxChannels = new RegExp(guildConfig.allowedSfxChannels); + if (!allowedSfxChannels.test(msg.channel.name)) return; + let sfx = msg.content.split(" ")[1]; - playing = true; - (function play(sfxFile) { - const dispatcher = msg.guild.voiceConnection.playFile( - sfxFile, - playOptions - ); - dispatcher - .on("end", reason => { - playing = false; - if (disconnectAfter) msg.guild.voiceConnection.disconnect(); - }) - .on("error", error => { - playing = false; - if (disconnectAfter) msg.guild.voiceConnection.disconnect(); - }) - .on("start", () => {}); - })(sfxPath.toString()); - }, - funfact: msg => { - if (funFacts.length > 0) { - // return random element from funFacts, unless one is specifically requested - let el; - let req = parseInt(msg.content.split(" ")[1]); - if (Number.isNaN(req) || typeof funFacts[req - 1] === "undefined") { - el = Math.floor(Math.random() * funFacts.length); - } else { - el = req - 1; + // retrieve sfx list from pastebin + if (sfx == "" || sfx === undefined) { + axios + .get("https://pastebin.com/raw/vRsZxrrw") + .then((res) => { + // break the result into half chunks if it exceeds the message limit size + // (the backticks take up 6 characters, discord limit is 2k) + let chunks = [res.data]; + if (res.data.length > 1994) { + chunks = chunkSubstr(res.data, res.data.length / 2); + } + + chunks.forEach((chunk) => { + return msg.channel.send("```" + chunk + "```"); + }); + }) + .catch(console.error); + + return true; } - let displayNum = (el + 1).toString(); - let funFact = funFacts[el]; - msg.channel - .send({ - embed: { - title: "FunFact #" + displayNum, - color: 0x21c629, - description: funFact - } - }) - .catch(console.error); - } else { - msg.channel.send("No fun facts found!"); - } - }, - hamfact: msg => { - if (hamFacts.length > 0) { - // return random element from hamFacts, unless one is specifically requested - let el; - let req = parseInt(msg.content.split(" ")[1]); - if (Number.isNaN(req) || typeof hamFacts[req - 1] === "undefined") { - el = Math.floor(Math.random() * hamFacts.length); + if (playing === true) + return msg.channel.send("Already playing, please wait."); + + // make sure this file exists either as an mp3 or wav + let sfxPath; + if (fs.existsSync(path.join(sfxFilePath, sfx + ".mp3"))) { + sfxPath = path.join(sfxFilePath, sfx + ".mp3"); + } else if (fs.existsSync(path.join(sfxFilePath, sfx + ".wav"))) { + sfxPath = path.join(sfxFilePath, sfx + ".wav"); } else { - el = req - 1; + return msg.reply("This sound effect does not exist!"); } - let displayNum = (el + 1).toString(); - let hamFact = hamFacts[el]; - msg.channel - .send({ - embed: { - title: "HamFact #" + displayNum, - color: 0x21c629, - description: hamFact - } - }) - .catch(console.error); - } else { - msg.channel.send("No ham facts found!"); - } - }, - dance: msg => { - msg.channel.send( - "*┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛*" - ); - }, - join: msg => { - if (!msg.guild.voiceConnection) { - joinVoiceChannel(msg) - .then(() => { - // - }) - .catch(console.error); - } else { - return msg.reply(`I'm already in a voice channel!`); - } - }, - leave: msg => { - if (msg.guild.voiceConnection) { - msg.content = "!sfx bye"; - commands.sfx(msg); - //msg.guild.voiceConnection.disconnect(); - } else { - return msg.reply(`If ya don't eat your meat, ya can't have any pudding!`); - } - }, - listen: msg => { - // listen for a particular member to speak and respond appropriately - if (msg.guild.voiceConnection) { - // get the guild member - //let guildMemberId = "88301001169207296"; // me - let guildMemberId = "153563292265086977"; // Screevo - let guildMember = msg.guild.members.get(guildMemberId); - if (guildMember) { - let listenInterval = 1000; - setInterval(() => { - if (guildMember.speaking === true) { - msg.content = "!sfx stfu"; - commands.sfx(msg, false); - } - }, listenInterval); + if (!msg.guild.voiceConnection) + return joinVoiceChannel(msg).then(() => commands.sfx(msg, guildConfig)); + + playing = true; + (function play(sfxFile) { + const dispatcher = msg.guild.voiceConnection.playFile(sfxFile, { + volume: guildConfig.sfxVolume, + passes: guildConfig.passes + }); + dispatcher + .on("end", (reason) => { + playing = false; + msg.guild.voiceConnection.disconnect(); + }) + .on("error", (error) => { + playing = false; + msg.guild.voiceConnection.disconnect(); + console.error("Error playing sfx: " + error); + }) + .on("start", () => {}); + })(sfxPath.toString()); + }, + funfact: (msg) => { + if (funFacts.length > 0) { + // return random element from funFacts, unless one is specifically requested + let el; + let req = parseInt(msg.content.split(" ")[1]); + if (Number.isNaN(req) || typeof funFacts[req - 1] === "undefined") { + el = Math.floor(Math.random() * funFacts.length); + } else { + el = req - 1; + } + + let displayNum = (el + 1).toString(); + let funFact = funFacts[el]; + msg.channel + .send({ + embed: { + title: "FunFact #" + displayNum, + color: 0x21c629, + description: funFact + } + }) + .catch(console.error); } else { - console.error( - `Could not find specified guild member: ${guildMemberId}!` - ); + msg.channel.send("No fun facts found!"); + } + }, + hamfact: (msg) => { + if (hamFacts.length > 0) { + // return random element from hamFacts, unless one is specifically requested + let el; + let req = parseInt(msg.content.split(" ")[1]); + if (Number.isNaN(req) || typeof hamFacts[req - 1] === "undefined") { + el = Math.floor(Math.random() * hamFacts.length); + } else { + el = req - 1; + } + + let displayNum = (el + 1).toString(); + let hamFact = hamFacts[el]; + msg.channel + .send({ + embed: { + title: "HamFact #" + displayNum, + color: 0x21c629, + description: hamFact + } + }) + .catch(console.error); + } else { + msg.channel.send("No ham facts found!"); + } + }, + dance: (msg) => { + msg.channel.send( + "*┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛*" + ); + }, + join: (msg) => { + if (!msg.guild.voiceConnection) { + joinVoiceChannel(msg) + .then(() => { + // + }) + .catch(console.error); + } else { + return msg.reply(`I'm already in a voice channel!`); + } + }, + leave: (msg) => { + if (msg.guild.voiceConnection) { msg.guild.voiceConnection.disconnect(); + } else { + return msg.reply( + `If ya don't eat your meat, ya can't have any pudding!` + ); } - } else { - // join the voice channel then call this command again - joinVoiceChannel(msg) - .then(() => { - commands.listen(msg); - }) - .catch(console.error); + }, + listen: (msg) => { + // listen for a particular member to speak and respond appropriately + if (msg.guild.voiceConnection) { + // get the guild member + //let guildMemberId = "88301001169207296"; // me + let guildMemberId = "153563292265086977"; // Screevo + let guildMember = msg.guild.members.get(guildMemberId); + if (guildMember) { + let listenInterval = 1000; + setInterval(() => { + if (guildMember.speaking === true) { + msg.content = "!sfx stfu"; + commands.sfx(msg, false); + } + }, listenInterval); + } else { + console.error( + `Could not find specified guild member: ${guildMemberId}!` + ); + msg.guild.voiceConnection.disconnect(); + } + } else { + // join the voice channel then call this command again + joinVoiceChannel(msg) + .then(() => { + commands.listen(msg); + }) + .catch(console.error); + } + }, + reboot: (msg) => { + if (msg.author.id == config.adminID) process.exit(); //Requires a node module like Forever to work. } - }, - reboot: msg => { - if (msg.author.id == config.adminID) process.exit(); //Requires a node module like Forever to work. - } -}; + }; -// Wait for discord to be ready, handle messages -client - .on("ready", () => { - console.log(`${config.botName} is connected and ready`); - let botChannel = client.channels.find("name", config.botChannel); + client + // Wait for discord to be ready, handle messages + .on("ready", () => { + console.log(`${config.botName} is connected and ready`); + }) // Listen for commands for the bot to respond to across all channels - }) - .on("message", msg => { - msg.originalContent = msg.content; - msg.content = msg.content.toLowerCase(); + .on("message", (msg) => { + // Ignore messages from unconfigured guilds + if (msg.guild) { + if (!config.discord.guilds[msg.guild.id]) { + return; + } + } else if (config.discord.handleDMs === false) { + return; + } - // Make sure it starts with the configured prefix - if (!msg.content.startsWith(config.prefix)) return; + msg.originalContent = msg.content; + msg.content = msg.content.toLowerCase(); - // And that it's not on cooldown - /*let cooldownKey = config.botName + msg.content + msg.channel.id; - cooldowns.get(cooldownKey, config.textCmdCooldown) - .then(onCooldown => { - if (onCooldown === false) {*/ - // Not on CD, check for native or static command - let commandNoPrefix = msg.content.slice(config.prefix.length).split(" ")[0]; - console.log( - `'${commandNoPrefix}' received in #${msg.channel.name} from @${msg.author.username}` - ); + // Find the guild config for this msg, use default if no guild (DM) + let guildConfig = msg.guild + ? config.discord.guilds[msg.guild.id] + : config.discord.guilds.default; - // check for native command first - if (commands.hasOwnProperty(commandNoPrefix)) { - commands[commandNoPrefix](msg); - // then a static command we've manually added - } else if (staticCommands.exists(commandNoPrefix)) { - let result = staticCommands.get(commandNoPrefix); - msg.channel - .send({ - embed: { - title: commandNoPrefix, - color: 0x21c629, - description: result - } - }) - .then( - sentMessage => {} /*cooldowns.set(cooldownKey, config.textCmdCooldown)*/ - ) - .catch(console.error); - // then a command exported from ankhbot - } else if (ankhbotCommands.exists(commandNoPrefix)) { - let result = ankhbotCommands.get(commandNoPrefix); - msg.channel - .send({ - embed: { - title: commandNoPrefix, - color: 0x21c629, - description: result - } - }) - .then( - sentMessage => {} /*cooldowns.set(cooldownKey, config.textCmdCooldown)*/ - ) - .catch(console.error); - } else { - // Not a command we recognize, ignore - } - /*} else { - // DM the user that it's on CD - dmUser(msg, `**${msg.content}** is currently on cooldown for another *${onCooldown} seconds!*`); + // Make sure it starts with the configured prefix + if (!msg.content.startsWith(guildConfig.prefix)) return; + + let commandNoPrefix = msg.content + .slice(guildConfig.prefix.length) + .split(" ")[0]; + + // check for native command first + if (commands.hasOwnProperty(commandNoPrefix)) { + console.log( + `'${commandNoPrefix}' received in ${guildConfig.internalName}#${msg.channel.name} from @${msg.author.username}` + ); + commands[commandNoPrefix](msg, guildConfig); + // then a static command we've manually added + } else if (staticCommands.exists(commandNoPrefix)) { + let result = staticCommands.get(commandNoPrefix); + console.log( + `'${commandNoPrefix}' received in ${guildConfig.internalName}#${msg.channel.name} from @${msg.author.username}` + ); + msg.channel + .send({ + embed: { + title: commandNoPrefix, + color: 0x21c629, + description: result + } + }) + .catch(console.error); + // then a command exported from ankhbot + } else if (ankhbotCommands.exists(commandNoPrefix)) { + let result = ankhbotCommands.get(commandNoPrefix); + msg.channel + .send({ + embed: { + title: commandNoPrefix, + color: 0x21c629, + description: result + } + }) + .catch(console.error); + } else { + // Not a command we recognize, ignore } }) - .catch(console.error);*/ - }) - .login(config.d_token); + // Handle new members joining one of our guilds + .on("guildMemberAdd", (member) => { + // Ignore events from unconfigured guilds + if (member.guild) { + if (!config.discord.guilds[member.guild.id]) { + return; + } + } else if (config.discord.handleDMs === false) { + return; + } + + console.log( + `A new member has joined '${member.guild.name}': ${member.displayName}` + ); + }) + // Log guild becoming unavailable (usually due to server outage) + .on("guildUnavailable", (guild) => { + console.log( + `Guild '${guild.name}' is no longer available! Most likely due to server outage.` + ); + }) + // Log debug messages if enabled + .on("debug", (info) => { + if (config.debug === true) { + console.log(`[${new Date()}] DEBUG: ${info}`); + } + }) + // Log disconnect event + .on("disconnect", (event) => { + console.log( + `Web Socket disconnected with code ${event.code} and reason '${event.reason}'` + ); + }) + // Log errors + .on("error", console.error) + // Log the bot in + .login(config.discord.token); +} function readSfxDirectory(path) { - let sfxList = fs.readdirSync(sfxFilePath); - sfxList.forEach(function(el, index, a) { + let thePath = path || sfxFilePath; + let sfxList = fs.readdirSync(thePath); + sfxList.forEach(function (el, index, a) { a[index] = el.split(".")[0]; }); return sfxList; @@ -302,8 +353,8 @@ function joinVoiceChannel(msg) { return msg.reply("I couldn't connect to your voice channel..."); voiceChannel .join() - .then(connection => resolve(connection)) - .catch(err => reject(err)); + .then((connection) => resolve(connection)) + .catch((err) => reject(err)); }); } @@ -312,7 +363,7 @@ function parseLines(filePath) { let lines = []; let data = fs.readFileSync(filePath, "utf-8"); let splitLines = data.toString().split("\n"); - splitLines.forEach(function(line) { + splitLines.forEach(function (line) { if (line.length > 0) { lines.push(line); } @@ -320,19 +371,8 @@ function parseLines(filePath) { return lines; } -function dmUser(originalMessage, newMessage) { - // check that this isn't already a DM before sending - if (originalMessage.channel.type === "dm") { - originalMessage.channel.send(newMessage); - } else { - originalMessage.member - .createDM() - .then(channel => { - channel.send(newMessage); - }) - .catch(console.log); - } -} - // catch Promise errors process.on("unhandledRejection", console.error); + +// Fire it up +init(config); diff --git a/sfx/anders.mp3 b/sfx/anders.mp3 new file mode 100755 index 0000000..c2c42a8 Binary files /dev/null and b/sfx/anders.mp3 differ diff --git a/sfx/blblbl.mp3 b/sfx/blblbl.mp3 new file mode 100755 index 0000000..b926d79 Binary files /dev/null and b/sfx/blblbl.mp3 differ diff --git a/sfx/chafe.mp3 b/sfx/chafe.mp3 new file mode 100755 index 0000000..f153f01 Binary files /dev/null and b/sfx/chafe.mp3 differ diff --git a/sfx/cooler.mp3 b/sfx/cooler.mp3 new file mode 100755 index 0000000..eaa216e Binary files /dev/null and b/sfx/cooler.mp3 differ diff --git a/sfx/dong.mp3 b/sfx/dong.mp3 new file mode 100755 index 0000000..8363872 Binary files /dev/null and b/sfx/dong.mp3 differ diff --git a/sfx/hop.mp3 b/sfx/hop.mp3 new file mode 100755 index 0000000..3054efa Binary files /dev/null and b/sfx/hop.mp3 differ diff --git a/sfx/long.mp3 b/sfx/long.mp3 new file mode 100755 index 0000000..ddeaa49 Binary files /dev/null and b/sfx/long.mp3 differ diff --git a/sfx/mayo.mp3 b/sfx/mayo.mp3 new file mode 100755 index 0000000..dd8ee67 Binary files /dev/null and b/sfx/mayo.mp3 differ diff --git a/sfx/obaeb.mp3 b/sfx/obaeb.mp3 index 8708081..70336ea 100755 Binary files a/sfx/obaeb.mp3 and b/sfx/obaeb.mp3 differ diff --git a/sfx/rentfree.mp3 b/sfx/rentfree.mp3 new file mode 100755 index 0000000..c9af1ba Binary files /dev/null and b/sfx/rentfree.mp3 differ diff --git a/sfx/tea.mp3 b/sfx/tea.mp3 new file mode 100755 index 0000000..5e1c68d Binary files /dev/null and b/sfx/tea.mp3 differ diff --git a/sfx/towel.mp3 b/sfx/towel.mp3 new file mode 100755 index 0000000..4a70e93 Binary files /dev/null and b/sfx/towel.mp3 differ