New functionalities

- added presence rotation
- added command permission
- added owner commands
- updated README
pull/11/head
Trivernis 6 years ago
parent 38fdece6c3
commit 2400429b72

@ -3,13 +3,10 @@ discordbot
A bot that does the discord thing. A bot that does the discord thing.
`node bot.js --token=DISCORDBOTTOKEN --ytapi=YOUTUBEAPIKEY` `node bot.js --token=<DiscordBotToken> --ytapi=<GoogleApiKey> [--owner=<DiscordTag>] [--prefix=<Char>] [--game=<String>]`
Ideas Ideas
--- ---
- an instance (class) of the bot for each server
- save data for each server (custom commands/playlists and so on)
- status rotator
- command replies saved in file (server specific file and global file) - command replies saved in file (server specific file and global file)
- reddit api - reddit api
- anilist api - anilist api

@ -3,20 +3,43 @@ const Discord = require("discord.js"),
logger = require('./lib/logging').getLogger(), logger = require('./lib/logging').getLogger(),
cmd = require("./lib/cmd"), cmd = require("./lib/cmd"),
guilding = require('./lib/guilding'), guilding = require('./lib/guilding'),
utils = require('./lib/utils'),
client = new Discord.Client(), client = new Discord.Client(),
args = require('args-parser')(process.argv), args = require('args-parser')(process.argv),
authToken = args.token, authToken = args.token,
prefix = args.prefix || '~', prefix = args.prefix || '~',
gamepresence = args.game || 'NieR:Automata'; gamepresence = args.game || prefix + 'help';
let presences = [],
rotator = null;
function main() { function main() {
utils.Cleanup(() => {
client.destroy();
});
cmd.setLogger(logger); cmd.setLogger(logger);
guilding.setLogger(logger); guilding.setLogger(logger);
cmd.init(prefix); cmd.init(prefix);
registerCommands(); registerCommands();
utils.dirExistence('./data', () => {
fs.exists('./data/presences.txt', (exist) => {
if (exist) {
logger.debug('Loading presences from file...');
let lineReader = require('readline').createInterface({
input: require('fs').createReadStream('./data/presences.txt')
});
lineReader.on('line', (line) => {
presences.push(line);
});
rotator = setInterval(() => rotatePresence(), 60000);
}
})
});
client.login(authToken).then(()=> { client.login(authToken).then(()=> {
logger.debug("Logged in"); logger.debug("Logged in");
}); });
} }
function registerCommands() { function registerCommands() {
@ -27,6 +50,40 @@ function registerCommands() {
cmd.createGlobalCommand(prefix + 'repeatafterme', (msg, argv, args) => { cmd.createGlobalCommand(prefix + 'repeatafterme', (msg, argv, args) => {
return args.join(' '); return args.join(' ');
},[], "Repeats what you say"); },[], "Repeats what you say");
cmd.createGlobalCommand(prefix + 'addpresence', (msg, argv, args) => {
let p = args.join(' ');
presences.push(p);
fs.writeFile('./data/presences.txt', presences.join('\n'), (err) => {});
return `Added Presence \`${p}\``;
},[], "Adds a presence to the rotation.", 'owner');
cmd.createGlobalCommand(prefix + 'shutdown', (msg) => {
msg.reply('Shutting down...').finally(() => {
logger.debug('Destroying client...');
client.destroy().finally(() => {
logger.debug(`Exiting Process...`);
process.exit(0);
});
});
},[], "Shuts the bot down.", 'owner');
cmd.createGlobalCommand(prefix + 'rotate', () => {
try {
clearInterval(rotator);
rotatePresence();
rotator = setInterval(() => rotatePresence(), 60000);
} catch (error) {
logger.warn(JSON.stringify(error));
}
}, [], 'Force presence rotation', 'owner');
}
function rotatePresence() {
let pr = presences.shift();
presences.push(pr);
client.user.setPresence({game: {name: `${gamepresence} | ${pr}`, type: "PLAYING"}, status: 'online'});
logger.debug(`Presence rotation to ${pr}`);
} }
client.on('ready', () => { client.on('ready', () => {
@ -40,7 +97,7 @@ client.on('message', msg => {
logger.verbose(`ME: ${msg.content}`); logger.verbose(`ME: ${msg.content}`);
return; return;
} }
logger.verbose(`<${msg.author.username}>: ${msg.content}`); logger.verbose(`<${msg.author.tag}>: ${msg.content}`);
if (!msg.guild) { if (!msg.guild) {
let reply = cmd.parseMessage(msg); let reply = cmd.parseMessage(msg);
if (reply) msg.channel.send(reply); if (reply) msg.channel.send(reply);

@ -2,7 +2,9 @@
/* Variable Definition */ /* Variable Definition */
let logger = require('winston'), let logger = require('winston'),
globCommands = {}; globCommands = {},
ownerCommands = {},
args = require('args-parser')(process.argv);
/* Function Definition */ /* Function Definition */
@ -14,12 +16,22 @@ exports.Servant = class {
this.commands = {}; this.commands = {};
this.createCommand(((prefix || '~') + 'help') || "~help", () => { this.createCommand(((prefix || '~') + 'help') || "~help", () => {
let helpstr = "```markdown\n"; let helpstr = "```markdown\n";
helpstr += "Commands\n---\n"; helpstr += "Commands\n===\n";
// TODO: Duplicate commands should not appear or make two sections, one with global commands, one with server commands helpstr += 'Global Commands\n---\n';
Object.entries(globCommands).concat(Object.entries(this.commands)).forEach(([key, value]) => { Object.entries(globCommands).sort().forEach(([key, value]) => {
if (value.role !== 'owner' || msg.author.tag === args.owner) {
let cmdhelp = `${key} [${value.args.join('] [')}]`.replace('[]', '').padEnd(25, ' '); let cmdhelp = `${key} [${value.args.join('] [')}]`.replace('[]', '').padEnd(25, ' ');
cmdhelp += value.description || ''; cmdhelp += value.description || '';
helpstr += `\n${cmdhelp}\n`; helpstr += `\n${cmdhelp}\n`;
}
});
helpstr += '\nServer Commands\n---\n';
Object.entries(this.commands).sort().forEach(([key, value]) => {
if (value.role !== 'owner' || msg.author.tag === args.owner) {
let cmdhelp = `${key} [${value.args.join('] [')}]`.replace('[]', '').padEnd(25, ' ');
cmdhelp += value.description || '';
helpstr += `\n${cmdhelp}\n`;
}
}); });
helpstr += "```"; helpstr += "```";
return helpstr; return helpstr;
@ -33,11 +45,12 @@ exports.Servant = class {
* @param args * @param args
* @param description * @param description
*/ */
createCommand(command, call, args, description) { createCommand(command, call, args, description, role) {
this.commands[command] = { this.commands[command] = {
'args': args, 'args': args,
'description': description, 'description': description,
'callback': call 'callback': call,
'role': role
}; };
} }
@ -61,6 +74,8 @@ exports.Servant = class {
let command = (content.match(/^.\w+/) || [])[0]; let command = (content.match(/^.\w+/) || [])[0];
if (!command || !this.commands[command]) return globResult; if (!command || !this.commands[command]) return globResult;
let cmd = this.commands[command]; let cmd = this.commands[command];
if (!checkPermission(msg, cmd.role)) return 'No Permission';
logger.debug(`Permission <${cmd.role || 'all'}> granted for command ${command} for user <${msg.author.tag}>`);
let argvars = content.match(/(?<= )\S+/g) || []; let argvars = content.match(/(?<= )\S+/g) || [];
let kwargs = {}; let kwargs = {};
let nLength = Math.min(cmd.args.length, argvars.length); let nLength = Math.min(cmd.args.length, argvars.length);
@ -88,12 +103,14 @@ exports.setLogger = function (newLogger) {
* @param call * @param call
* @param args * @param args
* @param description * @param description
* @param role
*/ */
exports.createGlobalCommand = function (command, call, args, description) { exports.createGlobalCommand = function (command, call, args, description, role) {
globCommands[command] = { globCommands[command] = {
'args': args || [], 'args': args || [],
'description': description, 'description': description,
'callback': call 'callback': call,
'role': role
}; };
logger.debug(`Created command: ${command}, args: ${args}`); logger.debug(`Created command: ${command}, args: ${args}`);
}; };
@ -113,13 +130,15 @@ exports.parseMessage = function (msg) {
*/ */
exports.init = function (prefix) { exports.init = function (prefix) {
logger.verbose("Created help command"); logger.verbose("Created help command");
this.createGlobalCommand((prefix + 'help') || "~help", () => { this.createGlobalCommand((prefix + 'help') || "~help", (msg) => {
let helpstr = "```markdown\n"; let helpstr = "```markdown\n";
helpstr += "Commands\n---\n"; helpstr += "Commands\n---\n";
Object.entries(globCommands).forEach(([key, value]) => { Object.entries(globCommands).sort().forEach(([key, value]) => {
if (value.role !== 'owner' || msg.author.tag === args.owner) {
let cmdhelp = `${key} [${value.args.join('] [')}]`.replace('[]', '').padEnd(25, ' '); let cmdhelp = `${key} [${value.args.join('] [')}]`.replace('[]', '').padEnd(25, ' ');
cmdhelp += value.description || ''; cmdhelp += value.description || '';
helpstr += `\n${cmdhelp}\n`; helpstr += `\n${cmdhelp}\n`;
}
}); });
helpstr += "```"; helpstr += "```";
return helpstr; return helpstr;
@ -135,6 +154,8 @@ function parseGlobalCommand(msg) {
let command = (content.match(/^.\w+/) || [])[0]; let command = (content.match(/^.\w+/) || [])[0];
if (!command || !globCommands[command]) return false; if (!command || !globCommands[command]) return false;
let cmd = globCommands[command]; let cmd = globCommands[command];
if (!checkPermission(msg, cmd.role)) return false;
logger.debug(`Permission <${cmd.role}> granted for command ${command} for user <${msg.author.tag}>`);
let argvars = content.match(/(?<= )\S+/g) || []; let argvars = content.match(/(?<= )\S+/g) || [];
let kwargs = {}; let kwargs = {};
let nLength = Math.min(cmd.args.length, argvars.length); let nLength = Math.min(cmd.args.length, argvars.length);
@ -145,3 +166,15 @@ function parseGlobalCommand(msg) {
logger.debug(`Executing callback for command: ${command}, kwargs: ${JSON.stringify(kwargs)}, argv: ${argv}`); logger.debug(`Executing callback for command: ${command}, kwargs: ${JSON.stringify(kwargs)}, argv: ${argv}`);
return cmd.callback(msg, kwargs, argv); return cmd.callback(msg, kwargs, argv);
} }
function checkPermission(msg, role) {
if (!role || ['all', 'any', 'everyone'].includes(role))
return true;
if (msg.author.tag === args.owner) {
return true;
} else {
if (msg.member && role && msg.member.roles.find("id", role.id))
return true
}
return false
}

@ -0,0 +1,72 @@
/**
* A Series of utility functions
*/
const fs = require('fs');
function noOp() {}
let sysdataPath = './res/data/sys.json';
let sysData = {};
/**
* returns the extension of a file for the given filename.
* @param {String} filename The name of the file.
* @return {String} A string that represents the file-extension.
*/
exports.getExtension = function (filename) {
if (!filename) return null;
try {
let exts = filename.match(/\.[a-z]+/g); // get the extension by using regex
if (exts) return exts[exts.length - 1]; // return the found extension
else return null; // return null if no extension could be found
} catch (error) {
console.error(error);
return null;
}
};
/**
* lets you define a cleanup for your program exit
* @param {Function} callback the cleanup function
* @constructor
* @author CanyonCasa & Pier-Luc Gendreau on StackOverflow
*/
exports.Cleanup = function Cleanup(callback) {
// attach user callback to the process event emitter
// if no callback, it will still exit gracefully on Ctrl-C
callback = callback || noOp;
process.on('cleanup',callback);
// do app specific cleaning before exiting
process.on('exit', function () {
process.emit('cleanup');
});
// catch ctrl+c event and exit normally
process.on('SIGINT', function () {
console.log('Ctrl-C...');
process.exit(2);
});
//catch uncaught exceptions, trace, then exit normally
process.on('uncaughtException', function(e) {
console.log('Uncaught Exception...');
console.log(e.stack);
process.exit(99);
});
};
/* FS */
exports.dirExistence = function(path, callback) {
fs.exists(path, (exist) => {
if (!exist) {
fs.mkdir(path, (err) => {
if (!err)
callback();
});
} else {
callback();
}
})
};
Loading…
Cancel
Save