support for ankhbot commands, static commands, cooldowns, implemented fun/ham facts

This commit is contained in:
Chris Ham
2018-01-22 18:32:09 -08:00
parent be5d8bc2ec
commit a2a9974108
10 changed files with 249 additions and 15 deletions

1
conf/ghbot.abcomg Executable file
View File

@@ -0,0 +1 @@
[{"Command":"!flavor","Permission":"Everyone","Info":"","Response":"All braise be to our Lord and Savior, Flavor!","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!stopit","Permission":"Everyone","Info":"","Response":"https://youtu.be/Ow0lr63y4Mw?t=6","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!nevergiveup","Permission":"Everyone","Info":"","Response":"https://www.youtube.com/watch?v=KxGRhd_iWuE","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!rip","Permission":"Everyone","Info":"","Response":"https://youtu.be/F-glHAzXi_M","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!ff4battle","Permission":"Everyone","Info":"","Response":"https://www.youtube.com/watch?v=__-NKgcRM2c","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!castlegiant","Permission":"Everyone","Info":"","Response":"https://www.twitch.tv/videos/61654438?t=06m20s","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!dash","Permission":"Everyone","Info":"","Response":"DASH!!! https://www.twitch.tv/videos/46709456","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!imdabes","Permission":"Everyone","Info":"","Response":"IM DA BES: https://www.youtube.com/watch?v=ZVUyyHYkBHk","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!dancebetch","Permission":"Everyone","Info":"","Response":"https://clips.twitch.tv/EsteemedIncredulousBaguetteTBTacoRight","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!rollindeep","Permission":"Everyone","Info":"","Response":"dear gods, that \"rollin' deep\" sounds like someone trying to be hip and failing worse than a dad joke mixed with an adult trying to be hip and \"with it\" to their high school aged children.","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!discord","Permission":"Everyone","Info":"","Response":"The Curing Chamber Discord: https://discord.gg/tDtyBss","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!twitter","Permission":"Everyone","Info":"","Response":"https://twitter.com/greenhamSRL","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!youtube","Permission":"Everyone","Info":"","Response":"https://www.youtube.com/channel/UC83WD1NtGDC54c7210BsuBQ","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!undressme","Permission":"Everyone","Info":"","Response":"https://clips.twitch.tv/LitigiousCooperativeKeyboardBloodTrail","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!donger","Permission":"Everyone","Info":"","Response":"ヽ༼ຈل͜ຈ༽ノ","CD":1,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!cleveland","Permission":"Everyone","Info":"","Response":"https://www.youtube.com/watch?v=ysmLA5TqbIY","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!nrg","Permission":"Everyone","Info":"","Response":"༼ つ ◕_◕ ༽つ STRIMMER TAKE MY ENERGY ༼ つ ◕_◕ ༽つ","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!late","Permission":"Everyone","Info":"","Response":"https://www.youtube.com/watch?v=wBqM2ytqHY4","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!store","Permission":"Everyone","Info":"","Response":"https://www.youtube.com/watch?v=iRZ2Sh5-XuM","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!slut","Permission":"Everyone","Info":"","Response":"@i_smoke_meth_daily69 HeyGuys","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"TC","Group":"DEFAULT","Cost":0},{"Command":"!antipb","Permission":"Everyone","Info":"","Response":"https://www.twitch.tv/videos/49126397","CD":1,"Enabled":true,"Count":0,"UCD":1,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!rboroutes","Permission":"Everyone","Info":"","Response":"Testing New Route w/New PW: https://docs.google.com/spreadsheets/d/1sPr5b_1vlrg65txCtiFiyDSmtbjBsjotJ5cbtJ3iQP0/edit?usp=sharing","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"TC","Group":"DEFAULT","Cost":0},{"Command":"!100mgroute","Permission":"Everyone","Info":"","Response":"https://docs.google.com/spreadsheets/d/1SD3Nzjc5tIes3Kw5uFnzke89gDxJeq02tvOSKfykd10/edit?usp=sharing","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0},{"Command":"!celeryman","Permission":"Everyone","Info":"","Response":"https://www.youtube.com/watch?v=maAFcEU6atk","CD":0,"Enabled":true,"Count":0,"UCD":0,"Usage":"A","Group":"DEFAULT","Cost":0}]

0
conf/text_commands Executable file
View File

View File

@@ -6,5 +6,6 @@
"passes": 2, "passes": 2,
"allowedSfxChannels": "bot|alttp-alerts", "allowedSfxChannels": "bot|alttp-alerts",
"sfxVolume": 0.3, "sfxVolume": 0.3,
"botChannel": "bot" "botChannel": "bot",
"textCmdCooldown": 5
} }

View File

@@ -7,4 +7,5 @@
"allowedSfxChannels": "bot", // channels where sfx can be used (separated by pipes) "allowedSfxChannels": "bot", // channels where sfx can be used (separated by pipes)
"sfxVolume": 0.3, "sfxVolume": 0.3,
"passes": 2, // can be increased to reduce packetloss at the expense of upload bandwidth, 4-5 should be lossless at the expense of 4-5x upload "passes": 2, // can be increased to reduce packetloss at the expense of upload bandwidth, 4-5 should be lossless at the expense of 4-5x upload
"textCmdCooldown": 5 // default cooldown in seconds for all text commands
} }

49
lib/ankhbot-commands.js Executable file
View File

@@ -0,0 +1,49 @@
module.exports = {
exists: exists,
get: get
};
const fs = require('fs'),
path = require('path');
const cmdPrefix = '!';
const commandsFilePath = path.join(__dirname, '..', 'conf', 'ghbot.abcomg');
let commands = parseCommands(commandsFilePath);
fs.watchFile(commandsFilePath, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
commands = require(commandsFilePath);
}
});
function exists(command)
{
let matches = commands.filter(obj => { return obj.Command === cmdPrefix+command; });
if (matches.length > 0) {
return true;
} else {
return false;
}
}
function get(command)
{
let matches = commands.filter(obj => { return obj.Command === cmdPrefix+command; });
if (matches.length > 0) {
let match = matches[0];
if (match.Enabled === true) {
return matches[0].Response;
} else {
return undefined;
}
} else {
return undefined;
}
}
// Read in the array of objects exported from AnkhBot
function parseCommands(filePath)
{
let data = fs.readFileSync(filePath, 'utf-8');
let commands = eval(data);
return commands;
}

48
lib/cooldowns.js Executable file
View File

@@ -0,0 +1,48 @@
module.exports = {
get: isOnCooldown,
set: placeOnCooldown
};
const memcache = require('memcache'),
md5 = require('md5'),
keyPrefix = 'cd';
const cache = new memcache.Client();
cache.on('error', console.error);
cache.connect();
// Given a cooldownTime in seconds and a command, returns false if the command is not on cooldown
// returns the time in seconds until the command will be ready again otherwise
function isOnCooldown(command, cooldownTime, callback)
{
return new Promise((resolve, reject) => {
let now = Date.now();
let onCooldown = false;
let key = keyPrefix + md5(command);
cache.get(key, function(err, timeUsed) {
if (err) reject(err);
if (timeUsed !== null) {
// Command was recently used, check timestamp to see if it's on cooldown
if ((now - timeUsed) <= (cooldownTime*1000)) {
// Calculate how much longer it's on cooldown
onCooldown = ((cooldownTime*1000) - (now - timeUsed))/1000;
}
}
resolve(onCooldown);
});
});
}
// Places a command on cooldown for cooldownTime (in seconds)
function placeOnCooldown(command, cooldownTime)
{
let key = keyPrefix + md5(command);
return cache.set(key, Date.now(), handleCacheSet, cooldownTime);
}
function handleCacheSet(error, result) {}
process.on('exit', (code) => {cache.close()});

53
lib/static-commands.js Executable file
View File

@@ -0,0 +1,53 @@
module.exports = {
exists: exists,
get: get
};
const fs = require('fs'),
path = require('path');
const commandsFilePath = path.join(__dirname, '..', 'conf', 'text_commands');
// Read in basic text commands / definitions and watch for changes
let commands = parseCommands(commandsFilePath);
fs.watchFile(commandsFilePath, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
commands = parseCommands(commandsFilePath);
}
});
// Read/parse text commands from the "database"
function parseCommands(filePath)
{
let commands = {};
let data = fs.readFileSync(filePath, 'utf-8');
let commandLines = data.toString().split('\n');
let commandParts;
let aliases;
commandLines.forEach(function(line) {
if (line.length > 0 && line.indexOf('|') !== -1) {
commandParts = line.split('|');
// check for aliases
aliases = commandParts[0].split(',');
aliases.forEach(function(cmd) {
commands[cmd] = commandParts[1];
//console.log(`!${cmd},`);
});
}
});
return commands;
}
function exists(command)
{
return commands.hasOwnProperty(command);
}
function get(command)
{
if (exists(command)) {
return commands[command];
} else {
return undefined;
}
}

82
main.js
View File

@@ -4,6 +4,9 @@ const { Client } = require('discord.js'),
path = require('path'), path = require('path'),
moment = require('moment'), moment = require('moment'),
timers = require('./lib/timers.js'), 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'); config = require('./config.json');
// Set up Discord client // Set up Discord client
@@ -94,7 +97,7 @@ const commands = {
let funFact = funFacts[el] let funFact = funFacts[el]
msg.channel.send({embed: { msg.channel.send({embed: {
"title": "FunFact #"+displayNum, "title": "FunFact #"+displayNum,
"color": 0xf30bff, "color": 0x21c629,
"description": funFact "description": funFact
}}).catch(console.error); }}).catch(console.error);
} else { } else {
@@ -115,14 +118,17 @@ const commands = {
let displayNum = (el+1).toString(); let displayNum = (el+1).toString();
let hamFact = hamFacts[el] let hamFact = hamFacts[el]
msg.channel.send({embed: { msg.channel.send({embed: {
"title": "hamFact #"+displayNum, "title": "HamFact #"+displayNum,
"color": 0xf30bff, "color": 0x21c629,
"description": hamFact "description": hamFact
}}).catch(console.error); }}).catch(console.error);
} else { } else {
msg.channel.send("No ham facts found!"); msg.channel.send("No ham facts found!");
} }
}, },
'dance': (msg) => {
msg.channel.send("*┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛*");
},
'reboot': (msg) => { 'reboot': (msg) => {
if (msg.author.id == config.adminID) process.exit(); //Requires a node module like Forever to work. if (msg.author.id == config.adminID) process.exit(); //Requires a node module like Forever to work.
} }
@@ -131,20 +137,54 @@ const commands = {
// Wait for discord to be ready, handle messages // Wait for discord to be ready, handle messages
client.on('ready', () => { client.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); let botChannel = client.channels.find('name', config.botChannel);
// Listen for commands for the bot to respond to across all channels
// Test timer
/*let timeToBlazeIt = moment().hour(16).minute(20).second(0).valueOf();
timers.onceAndRepeat(timeToBlazeIt, 86400, 'blazeit')
.on('blazeit', () => {
let emoji = client.guilds.first().emojis.find('name', 'BlazedHam');
alertsChannel.send(`You know what time it is. ${emoji}`);
});*/
}).on('message', msg => { }).on('message', msg => {
msg.originalContent = msg.content;
msg.content = msg.content.toLowerCase();
// Make sure it starts with the configured prefix
if (!msg.content.startsWith(config.prefix)) return; if (!msg.content.startsWith(config.prefix)) return;
let cmd = msg.content.toLowerCase().slice(config.prefix.length).split(' ')[0];
if (commands.hasOwnProperty(cmd)) commands[cmd](msg); // And that it's not on cooldown
let cooldownKey = 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}`);
// 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!*`);
}
})
.catch(console.error);
}); });
client.login(config.d_token); client.login(config.d_token);
@@ -179,3 +219,17 @@ 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);
}
}

25
package-lock.json generated
View File

@@ -60,6 +60,11 @@
"version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
}, },
"charenc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
},
"co": { "co": {
"version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
@@ -88,6 +93,11 @@
"version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}, },
"crypt": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
},
"cryptiles": { "cryptiles": {
"version": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
"integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=" "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g="
@@ -198,6 +208,11 @@
"version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"is-typedarray": { "is-typedarray": {
"version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
@@ -245,6 +260,16 @@
"version": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", "version": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s="
}, },
"md5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
"integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk="
},
"memcache": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/memcache/-/memcache-0.3.0.tgz",
"integrity": "sha1-vbuXjqS+4P3TFmmXsYg9KX4IWdw="
},
"methods": { "methods": {
"version": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "version": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="

View File

@@ -6,6 +6,8 @@
"dependencies": { "dependencies": {
"discord.js": "^11.1.0", "discord.js": "^11.1.0",
"ffmpeg-binaries": "^3.2.2-3", "ffmpeg-binaries": "^3.2.2-3",
"md5": "^2.2.1",
"memcache": "^0.3.0",
"moment": "^2.18.1", "moment": "^2.18.1",
"node-opus": "^0.2.6", "node-opus": "^0.2.6",
"request": "^2.81.0" "request": "^2.81.0"