multi-guild support, new sfx

This commit is contained in:
Chris Ham
2020-05-01 16:01:38 -07:00
parent 11ccf684b5
commit de193c1099
13 changed files with 309 additions and 269 deletions

View File

@@ -3,293 +3,344 @@ const { Client } = require("discord.js"),
fs = require("fs"), fs = require("fs"),
path = require("path"), path = require("path"),
axios = require("axios"), axios = require("axios"),
moment = require("moment"),
timers = require("./lib/timers.js"),
staticCommands = require("./lib/static-commands.js"), staticCommands = require("./lib/static-commands.js"),
//cooldowns = require('./lib/cooldowns.js'),
ankhbotCommands = require("./lib/ankhbot-commands.js"), ankhbotCommands = require("./lib/ankhbot-commands.js"),
config = require("./config.json"); config = require("./config.json");
// Set up Discord client function chunkSubstr(str, size) {
const client = new Client(); const numChunks = Math.ceil(str.length / size);
const chunks = new Array(numChunks);
// Set up SFX for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
const sfxFilePath = path.join(__dirname, "sfx"); chunks[i] = str.substr(o, size);
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);
} }
});
// @todo DRY this shit up return chunks;
}
// Read in fun facts function init(config) {
const funFactsFilePath = path.join(__dirname, "conf", "funfacts"); // Set up Discord client
let funFacts = parseLines(funFactsFilePath); const client = new Client();
fs.watchFile(funFactsFilePath, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
funFacts = parseLines(funFactsFilePath);
}
});
// Read in ham facts // Set up SFX
const hamFactsFilePath = path.join(__dirname, "conf", "hamfacts"); const sfxFilePath = path.join(__dirname, "sfx");
let hamFacts = parseLines(hamFactsFilePath); let playing = false;
fs.watchFile(hamFactsFilePath, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
hamFacts = parseLines(hamFactsFilePath);
}
});
// Set up the native commands to handle // Read in sfx directory, filenames are the commands
const commands = { let sfxList = readSfxDirectory(sfxFilePath);
sfx: (msg, disconnectAfter) => { // Watch directory for changes and update the list
if (!allowedSfxChannels.test(msg.channel.name)) return; fs.watch(sfxFilePath, (eventType, filename) => {
let sfx = msg.content.split(" ")[1]; if (eventType === "rename") {
sfxList = readSfxDirectory(sfxFilePath);
// 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;
} }
});
if (playing === true) // @todo DRY this shit up
return msg.channel.send("Already playing, please wait.");
// make sure this file exists either as an mp3 or wav // Read in fun facts
let sfxPath; const funFactsFilePath = path.join(__dirname, "conf", "funfacts");
if (fs.existsSync(path.join(sfxFilePath, sfx + ".mp3"))) { let funFacts = parseLines(funFactsFilePath);
sfxPath = path.join(sfxFilePath, sfx + ".mp3"); fs.watchFile(funFactsFilePath, (curr, prev) => {
} else if (fs.existsSync(path.join(sfxFilePath, sfx + ".wav"))) { if (curr.mtime !== prev.mtime) {
sfxPath = path.join(sfxFilePath, sfx + ".wav"); funFacts = parseLines(funFactsFilePath);
} else {
return msg.reply("This sound effect does not exist!");
} }
});
if (!msg.guild.voiceConnection) // Read in ham facts
return joinVoiceChannel(msg).then(() => const hamFactsFilePath = path.join(__dirname, "conf", "hamfacts");
commands.sfx(msg, disconnectAfter) let hamFacts = parseLines(hamFactsFilePath);
); fs.watchFile(hamFactsFilePath, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
hamFacts = parseLines(hamFactsFilePath);
}
});
disconnectAfter = // Set up the native commands to handle
typeof disconnectAfter !== "undefined" ? disconnectAfter : true; 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; // retrieve sfx list from pastebin
(function play(sfxFile) { if (sfx == "" || sfx === undefined) {
const dispatcher = msg.guild.voiceConnection.playFile( axios
sfxFile, .get("https://pastebin.com/raw/vRsZxrrw")
playOptions .then((res) => {
); // break the result into half chunks if it exceeds the message limit size
dispatcher // (the backticks take up 6 characters, discord limit is 2k)
.on("end", reason => { let chunks = [res.data];
playing = false; if (res.data.length > 1994) {
if (disconnectAfter) msg.guild.voiceConnection.disconnect(); chunks = chunkSubstr(res.data, res.data.length / 2);
}) }
.on("error", error => {
playing = false; chunks.forEach((chunk) => {
if (disconnectAfter) msg.guild.voiceConnection.disconnect(); return msg.channel.send("```" + chunk + "```");
}) });
.on("start", () => {}); })
})(sfxPath.toString()); .catch(console.error);
},
funfact: msg => { return true;
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(); if (playing === true)
let funFact = funFacts[el]; return msg.channel.send("Already playing, please wait.");
msg.channel
.send({ // make sure this file exists either as an mp3 or wav
embed: { let sfxPath;
title: "FunFact #" + displayNum, if (fs.existsSync(path.join(sfxFilePath, sfx + ".mp3"))) {
color: 0x21c629, sfxPath = path.join(sfxFilePath, sfx + ".mp3");
description: funFact } else if (fs.existsSync(path.join(sfxFilePath, sfx + ".wav"))) {
} sfxPath = path.join(sfxFilePath, sfx + ".wav");
})
.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);
} else { } else {
el = req - 1; return msg.reply("This sound effect does not exist!");
} }
let displayNum = (el + 1).toString(); if (!msg.guild.voiceConnection)
let hamFact = hamFacts[el]; return joinVoiceChannel(msg).then(() => commands.sfx(msg, guildConfig));
msg.channel
.send({ playing = true;
embed: { (function play(sfxFile) {
title: "HamFact #" + displayNum, const dispatcher = msg.guild.voiceConnection.playFile(sfxFile, {
color: 0x21c629, volume: guildConfig.sfxVolume,
description: hamFact passes: guildConfig.passes
} });
}) dispatcher
.catch(console.error); .on("end", (reason) => {
} else { playing = false;
msg.channel.send("No ham facts found!"); msg.guild.voiceConnection.disconnect();
} })
}, .on("error", (error) => {
dance: msg => { playing = false;
msg.channel.send( msg.guild.voiceConnection.disconnect();
"*┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛*" console.error("Error playing sfx: " + error);
); })
}, .on("start", () => {});
join: msg => { })(sfxPath.toString());
if (!msg.guild.voiceConnection) { },
joinVoiceChannel(msg) funfact: (msg) => {
.then(() => { if (funFacts.length > 0) {
// // return random element from funFacts, unless one is specifically requested
}) let el;
.catch(console.error); let req = parseInt(msg.content.split(" ")[1]);
} else { if (Number.isNaN(req) || typeof funFacts[req - 1] === "undefined") {
return msg.reply(`I'm already in a voice channel!`); el = Math.floor(Math.random() * funFacts.length);
} } else {
}, el = req - 1;
leave: msg => { }
if (msg.guild.voiceConnection) {
msg.content = "!sfx bye"; let displayNum = (el + 1).toString();
commands.sfx(msg); let funFact = funFacts[el];
//msg.guild.voiceConnection.disconnect(); msg.channel
} else { .send({
return msg.reply(`If ya don't eat your meat, ya can't have any pudding!`); embed: {
} title: "FunFact #" + displayNum,
}, color: 0x21c629,
listen: msg => { description: funFact
// listen for a particular member to speak and respond appropriately }
if (msg.guild.voiceConnection) { })
// get the guild member .catch(console.error);
//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 { } else {
console.error( msg.channel.send("No fun facts found!");
`Could not find specified guild member: ${guildMemberId}!` }
); },
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(); 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 listen: (msg) => {
joinVoiceChannel(msg) // listen for a particular member to speak and respond appropriately
.then(() => { if (msg.guild.voiceConnection) {
commands.listen(msg); // get the guild member
}) //let guildMemberId = "88301001169207296"; // me
.catch(console.error); 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
client // Wait for discord to be ready, handle messages
.on("ready", () => { .on("ready", () => {
console.log(`${config.botName} is connected and ready`); console.log(`${config.botName} is connected and ready`);
let botChannel = client.channels.find("name", config.botChannel); })
// Listen for commands for the bot to respond to across all channels // Listen for commands for the bot to respond to across all channels
}) .on("message", (msg) => {
.on("message", msg => { // Ignore messages from unconfigured guilds
msg.originalContent = msg.content; if (msg.guild) {
msg.content = msg.content.toLowerCase(); if (!config.discord.guilds[msg.guild.id]) {
return;
}
} else if (config.discord.handleDMs === false) {
return;
}
// Make sure it starts with the configured prefix msg.originalContent = msg.content;
if (!msg.content.startsWith(config.prefix)) return; msg.content = msg.content.toLowerCase();
// And that it's not on cooldown // Find the guild config for this msg, use default if no guild (DM)
/*let cooldownKey = config.botName + msg.content + msg.channel.id; let guildConfig = msg.guild
cooldowns.get(cooldownKey, config.textCmdCooldown) ? config.discord.guilds[msg.guild.id]
.then(onCooldown => { : config.discord.guilds.default;
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}`
);
// check for native command first // Make sure it starts with the configured prefix
if (commands.hasOwnProperty(commandNoPrefix)) { if (!msg.content.startsWith(guildConfig.prefix)) return;
commands[commandNoPrefix](msg);
// then a static command we've manually added let commandNoPrefix = msg.content
} else if (staticCommands.exists(commandNoPrefix)) { .slice(guildConfig.prefix.length)
let result = staticCommands.get(commandNoPrefix); .split(" ")[0];
msg.channel
.send({ // check for native command first
embed: { if (commands.hasOwnProperty(commandNoPrefix)) {
title: commandNoPrefix, console.log(
color: 0x21c629, `'${commandNoPrefix}' received in ${guildConfig.internalName}#${msg.channel.name} from @${msg.author.username}`
description: result );
} commands[commandNoPrefix](msg, guildConfig);
}) // then a static command we've manually added
.then( } else if (staticCommands.exists(commandNoPrefix)) {
sentMessage => {} /*cooldowns.set(cooldownKey, config.textCmdCooldown)*/ let result = staticCommands.get(commandNoPrefix);
) console.log(
.catch(console.error); `'${commandNoPrefix}' received in ${guildConfig.internalName}#${msg.channel.name} from @${msg.author.username}`
// then a command exported from ankhbot );
} else if (ankhbotCommands.exists(commandNoPrefix)) { msg.channel
let result = ankhbotCommands.get(commandNoPrefix); .send({
msg.channel embed: {
.send({ title: commandNoPrefix,
embed: { color: 0x21c629,
title: commandNoPrefix, description: result
color: 0x21c629, }
description: result })
} .catch(console.error);
}) // then a command exported from ankhbot
.then( } else if (ankhbotCommands.exists(commandNoPrefix)) {
sentMessage => {} /*cooldowns.set(cooldownKey, config.textCmdCooldown)*/ let result = ankhbotCommands.get(commandNoPrefix);
) msg.channel
.catch(console.error); .send({
} else { embed: {
// Not a command we recognize, ignore title: commandNoPrefix,
} color: 0x21c629,
/*} else { description: result
// DM the user that it's on CD }
dmUser(msg, `**${msg.content}** is currently on cooldown for another *${onCooldown} seconds!*`); })
.catch(console.error);
} else {
// Not a command we recognize, ignore
} }
}) })
.catch(console.error);*/ // Handle new members joining one of our guilds
}) .on("guildMemberAdd", (member) => {
.login(config.d_token); // 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) { function readSfxDirectory(path) {
let sfxList = fs.readdirSync(sfxFilePath); let thePath = path || sfxFilePath;
sfxList.forEach(function(el, index, a) { let sfxList = fs.readdirSync(thePath);
sfxList.forEach(function (el, index, a) {
a[index] = el.split(".")[0]; a[index] = el.split(".")[0];
}); });
return sfxList; return sfxList;
@@ -302,8 +353,8 @@ function joinVoiceChannel(msg) {
return msg.reply("I couldn't connect to your voice channel..."); return msg.reply("I couldn't connect to your voice channel...");
voiceChannel voiceChannel
.join() .join()
.then(connection => resolve(connection)) .then((connection) => resolve(connection))
.catch(err => reject(err)); .catch((err) => reject(err));
}); });
} }
@@ -312,7 +363,7 @@ function parseLines(filePath) {
let lines = []; let lines = [];
let data = fs.readFileSync(filePath, "utf-8"); let data = fs.readFileSync(filePath, "utf-8");
let splitLines = data.toString().split("\n"); let splitLines = data.toString().split("\n");
splitLines.forEach(function(line) { splitLines.forEach(function (line) {
if (line.length > 0) { if (line.length > 0) {
lines.push(line); lines.push(line);
} }
@@ -320,19 +371,8 @@ function parseLines(filePath) {
return lines; 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 // catch Promise errors
process.on("unhandledRejection", console.error); process.on("unhandledRejection", console.error);
// Fire it up
init(config);

BIN
sfx/anders.mp3 Executable file

Binary file not shown.

BIN
sfx/blblbl.mp3 Executable file

Binary file not shown.

BIN
sfx/chafe.mp3 Executable file

Binary file not shown.

BIN
sfx/cooler.mp3 Executable file

Binary file not shown.

BIN
sfx/dong.mp3 Executable file

Binary file not shown.

BIN
sfx/hop.mp3 Executable file

Binary file not shown.

BIN
sfx/long.mp3 Executable file

Binary file not shown.

BIN
sfx/mayo.mp3 Executable file

Binary file not shown.

Binary file not shown.

BIN
sfx/rentfree.mp3 Executable file

Binary file not shown.

BIN
sfx/tea.mp3 Executable file

Binary file not shown.

BIN
sfx/towel.mp3 Executable file

Binary file not shown.