Merge pull request #11 from Trivernis/develop

Version 0.9.1
pull/14/head
Trivernis 6 years ago committed by GitHub
commit 131e0d6795
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,16 +3,11 @@ discordbot
A bot that does the discord thing.
`node bot.js --token=DICORDBOTTOKEN --ytapi=YOUTUBEAPIKEY`
`node bot.js --token=<DiscordBotToken> --ytapi=<GoogleApiKey> [--owner=<DiscordTag>] [--prefix=<Char>] [--game=<String>]`
Ideas
---
- an instance (class) of the bot for each server
- planned with datahandler, could be for the whole server in the future
- save data for each server (custom commands/playlists and so on)
- custom scripts for each server (?) <- not that hard to code (with the right api)
- status rotator
-
- command replies saved in file (server specific file and global file)
- reddit api
- anilist api
- othercoolstuff api

@ -3,30 +3,88 @@ const Discord = require("discord.js"),
logger = require('./lib/logging').getLogger(),
cmd = require("./lib/cmd"),
guilding = require('./lib/guilding'),
utils = require('./lib/utils'),
client = new Discord.Client(),
args = require('args-parser')(process.argv),
authToken = args.token,
prefix = args.prefix || '~',
gamepresence = args.game || 'NieR:Automata';
gamepresence = args.game || prefix + 'help';
let presences = [],
rotator = null;
function main() {
utils.Cleanup(() => {
client.destroy();
});
cmd.setLogger(logger);
guilding.setLogger(logger);
cmd.init(prefix);
registerCommands();
client.login(authToken).then(()=> {
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(() => {
logger.debug("Logged in");
});
}
function registerCommands() {
cmd.createGlobalCommand(prefix + 'ping', () => {
return 'Pong!';
return 'Pong!';
}, [], "Try it yourself.");
cmd.createGlobalCommand(prefix + 'repeatafterme', (msg, argv, args) => {
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', () => {
@ -40,7 +98,7 @@ client.on('message', msg => {
logger.verbose(`ME: ${msg.content}`);
return;
}
logger.verbose(`<${msg.author.username}>: ${msg.content}`);
logger.verbose(`<${msg.author.tag}>: ${msg.content}`);
if (!msg.guild) {
let reply = cmd.parseMessage(msg);
if (reply) msg.channel.send(reply);

@ -2,7 +2,9 @@
/* Variable Definition */
let logger = require('winston'),
globCommands = {};
globCommands = {},
ownerCommands = {},
args = require('args-parser')(process.argv);
/* Function Definition */
@ -12,13 +14,24 @@ let logger = require('winston'),
exports.Servant = class {
constructor(prefix) {
this.commands = {};
this.createCommand(((prefix || '~')+'help') || "~help", () => {
this.createCommand(((prefix || '~') + 'help') || "~help", () => {
let helpstr = "```markdown\n";
helpstr += "Commands\n---\n";
Object.entries(globCommands).concat(Object.entries(this.commands)).forEach(([key, value]) => {
let cmdhelp = `${key} [${value.args.join('] [')}]`.replace('[]', '').padEnd(25, ' ');
cmdhelp += value.description || '';
helpstr += `\n${cmdhelp}\n`;
helpstr += "Commands\n===\n";
helpstr += 'Global Commands\n---\n';
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, ' ');
cmdhelp += value.description || '';
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 += "```";
return helpstr;
@ -32,11 +45,12 @@ exports.Servant = class {
* @param args
* @param description
*/
createCommand(command, call, args, description) {
this.commands[command] = {
createCommand(command, call, args, description, role) {
this.commands[command] = {
'args': args,
'description': description,
'callback': call
'callback': call,
'role': role
};
}
@ -60,6 +74,8 @@ exports.Servant = class {
let command = (content.match(/^.\w+/) || [])[0];
if (!command || !this.commands[command]) return globResult;
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 kwargs = {};
let nLength = Math.min(cmd.args.length, argvars.length);
@ -77,7 +93,7 @@ exports.Servant = class {
* Getting the logger
* @param {Object} newLogger
*/
exports.setLogger = function(newLogger) {
exports.setLogger = function (newLogger) {
logger = newLogger;
};
@ -87,12 +103,14 @@ exports.setLogger = function(newLogger) {
* @param call
* @param args
* @param description
* @param role
*/
exports.createGlobalCommand = function(command, call, args, description) {
globCommands[command] = {
exports.createGlobalCommand = function (command, call, args, description, role) {
globCommands[command] = {
'args': args || [],
'description': description,
'callback': call
'callback': call,
'role': role
};
logger.debug(`Created command: ${command}, args: ${args}`);
};
@ -103,22 +121,24 @@ exports.createGlobalCommand = function(command, call, args, description) {
* @param msg
* @returns {boolean|*}
*/
exports.parseMessage = function(msg) {
exports.parseMessage = function (msg) {
return parseGlobalCommand(msg);
}
};
/**
* Initializes the module by creating a help command
*/
exports.init = function(prefix) {
exports.init = function (prefix) {
logger.verbose("Created help command");
this.createGlobalCommand((prefix+'help') || "~help", () => {
this.createGlobalCommand((prefix + 'help') || "~help", (msg) => {
let helpstr = "```markdown\n";
helpstr += "Commands\n---\n";
Object.entries(globCommands).forEach(([key, value]) => {
let cmdhelp = `${key} [${value.args.join('] [')}]`.replace('[]', '').padEnd(25, ' ');
cmdhelp += value.description || '';
helpstr += `\n${cmdhelp}\n`;
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, ' ');
cmdhelp += value.description || '';
helpstr += `\n${cmdhelp}\n`;
}
});
helpstr += "```";
return helpstr;
@ -134,6 +154,8 @@ function parseGlobalCommand(msg) {
let command = (content.match(/^.\w+/) || [])[0];
if (!command || !globCommands[command]) return false;
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 kwargs = {};
let nLength = Math.min(cmd.args.length, argvars.length);
@ -143,4 +165,16 @@ function parseGlobalCommand(msg) {
let argv = argvars.slice(nLength);
logger.debug(`Executing callback for command: ${command}, kwargs: ${JSON.stringify(kwargs)}, argv: ${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
}

@ -41,23 +41,47 @@ exports.DataHandler = class {
}
}
/**
* adds an entry to the fileEntries. refreshes the entrie file
* @param name
* @param path
*/
addEntry(name, path) {
this.fileEntries.name = path;
this.refreshEntries();
}
/**
* shortcut function to join the path with the working directory
* @param file
* @returns {Promise<VoiceConnection> | string}
*/
getfp(file) {
return path.join(this.workingDir, file);
}
/**
* shortcut function that evokes the callback after reading the file. the files path is the name
* joined with the working directory
* @param file
* @param callback
*/
getcont(file, callback) {
fs.readFile(this.getfp, 'utf-8', callback);
}
/**
* returns the JSON content of a file in the working directory
* @param file
* @returns {any}
*/
getJSONSync(file) {
return JSON.parse(fs.readFileSync(this.getfp(file), 'utf-8'));
}
/**
* writes all entris of the fileEntries variable into the fileEntries file.
*/
refreshEntries() {
fs.writeFile(this.getfp(entryfile), JSON.stringify(this.fileEntries), (err) => {
if (err)
@ -65,6 +89,11 @@ exports.DataHandler = class {
});
}
/**
* returns the data for the entry <name>
* @param name
* @returns {*}
*/
getData(name) {
try {
if (this.fileData[name])
@ -79,7 +108,11 @@ exports.DataHandler = class {
}
}
/**
* sets the entry <name> to data
* @param name
* @param data
*/
setData(name, data) {
this.fileData[name] = data;
if (!this.fileEntries[name]) {

@ -9,6 +9,7 @@ exports.setLogger = function (newLogger) {
music.setLogger(logger);
};
exports.GuildHandler = class {
constructor(guild, prefix) {
this.guild = guild;
@ -20,22 +21,42 @@ exports.GuildHandler = class {
this.registerMusicCommands();
}
/**
* function shortcut returns the data from the dataHandler
* @param name
* @returns {{}}
*/
getData(name) {
return this.dataHandler.getData(name);
}
/**
* appends data to the data handler
* @param name
* @param key
* @param value
*/
appendData(name, key, value) {
let data = this.getData(name);
data[key] = value;
this.dataHandler.setData(name, data);
}
/**
* deletes an entry from the data handler
* @param name
* @param key
*/
deleteDataEntry(name, key) {
let data = this.getData(name);
delete data[key];
this.dataHandler.setData(name, data);
}
/**
* registers all music commands and initializes a dj
* @param cmdPrefix
*/
registerMusicCommands(cmdPrefix) {
let prefix = cmdPrefix || this.prefix;
this.dj = new music.DJ();
@ -95,7 +116,7 @@ exports.GuildHandler = class {
if (msg.member.voiceChannel) {
this.dj.connect(msg.member.voiceChannel);
} else {
msg.reply("You are not connected to a voicechannel.");
return "You are not connected to a voicechannel.";
}
}, [], "Joins the VC you are in.");
@ -161,20 +182,32 @@ exports.GuildHandler = class {
// saved command - prints out saved playlists
this.createCommand(prefix + 'saved', () => {
let response = '```markdown\nSaved Playlists:\n==\n'
let response = '```markdown\nSaved Playlists:\n==\n';
Object.entries(this.getData('savedplaylists')).forEach(([key, value]) => {
response += `${key.padEnd(10, ' ')} ${value} \n\n`;
});
response += '```'
response += '```';
return response;
}, [], "Prints out all saved playlists.");
}
/**
* creates a servant if not set and lets the servant create a command
* @param command
* @param call
* @param args
* @param description
*/
createCommand(command, call, args, description) {
if (!this.servant) this.servant = new cmd.Servant(this.prefix);
this.servant.createCommand(command, call, args, description);
}
/**
* handles the message by letting the servant parse the command. Depending on the message setting it
* replies or just sends the answer.
* @param msg
*/
handleMessage(msg) {
if (!this.servant) this.servant = new cmd.Servant(this.prefix);
let answer = this.servant.parseCommand(msg);

@ -50,7 +50,7 @@ const winston = require('winston'),
* A function to return the logger that has been created after appending an exception handler
* @returns {Object}
*/
exports.getLogger = function() {
exports.getLogger = function () {
logger.exceptions.handle(
new winston.transports.File({
filename: './.log/exceptions.log'

@ -11,7 +11,7 @@ let connections = {};
/* Function Definition */
exports.DJ = class{
exports.DJ = class {
constructor(voiceChannel) {
this.conn = null;
this.disp = null;
@ -80,7 +80,7 @@ exports.DJ = class{
setTimeout(() => {
if (this.voiceChannel && this.voiceChannel.members.size === 1)
logger.verbose(`Exiting ${this.voiceChannel.name}`);
this.stop();
this.stop();
}, 300000);
} else if (this.connected)
setTimeout(() => this.checkListeners(), 10000);
@ -226,7 +226,7 @@ exports.DJ = class{
this.voiceChannel.leave();
logger.debug("Left VoiceChannel");
}
} catch(error) {
} catch (error) {
logger.verbose(JSON.stringify(error));
}
}
@ -236,7 +236,7 @@ exports.DJ = class{
* end event of the dispatcher that automatically plays the next song. If no dispatcher is found
* It tries to play the next song with playYouTube
*/
skip () {
skip() {
logger.debug("Skipping song");
if (this.disp !== null) {
this.disp.end();
@ -284,7 +284,7 @@ exports.DJ = class{
clear() {
this.queue = [];
}
}
};
/**
* Getting the logger;
@ -298,7 +298,7 @@ exports.setLogger = function (newLogger) {
* Connects to a voicechannel
* @param voiceChannel
*/
exports.connect = function(voiceChannel) {
exports.connect = function (voiceChannel) {
let gid = voiceChannel.guild.id;
let voiceDJ = new this.DJ(voiceChannel);
djs[gid] = voiceDJ;
@ -310,7 +310,7 @@ exports.connect = function(voiceChannel) {
* @param filename
* @param guildId
*/
exports.playFile = function(guildId, filename) {
exports.playFile = function (guildId, filename) {
djs[guildId].playFile(filename);
};
@ -319,7 +319,7 @@ exports.playFile = function(guildId, filename) {
* @param voiceChannel
* @param url
*/
exports.play = function(voiceChannel, url) {
exports.play = function (voiceChannel, url) {
let guildId = voiceChannel.guild.id;
if (!djs[guildId]) {
this.connect(voiceChannel).then(() => {
@ -335,7 +335,7 @@ exports.play = function(voiceChannel, url) {
* @param voiceChannel
* @param url
*/
exports.playnext = function(voiceChannel, url) {
exports.playnext = function (voiceChannel, url) {
let guildId = voiceChannel.guild.id;
if (!djs[guildId]) {
this.connect(voiceChannel).then(() => {
@ -351,14 +351,14 @@ exports.playnext = function(voiceChannel, url) {
* @param percentage
* @param guildId
*/
exports.setVolume = function(guildId, percentage) {
exports.setVolume = function (guildId, percentage) {
djs[guildId].setVolume(percentage);
};
/**
* pauses the music
*/
exports.pause = function(guildId) {
exports.pause = function (guildId) {
djs[guildId].pause();
};
@ -366,7 +366,7 @@ exports.pause = function(guildId) {
* Resumes the music
* @param guildId
*/
exports.resume = function(guildId) {
exports.resume = function (guildId) {
djs[guildId].resume();
};
@ -374,7 +374,7 @@ exports.resume = function(guildId) {
* Stops the music
* @param guildId
*/
exports.stop = function(guildId) {
exports.stop = function (guildId) {
djs[guildId].stop();
delete djs[guildId];
};
@ -383,7 +383,7 @@ exports.stop = function(guildId) {
* Skips the song
* @param guildId
*/
exports.skip = function(guildId) {
exports.skip = function (guildId) {
djs[guildId].skip();
};
@ -391,7 +391,7 @@ exports.skip = function(guildId) {
* Clears the playlist
* @param guildId
*/
exports.clearQueue = function(guildId) {
exports.clearQueue = function (guildId) {
djs[guildId].clear();
};
@ -399,7 +399,7 @@ exports.clearQueue = function(guildId) {
* Returns the queue
* @param guildId
*/
exports.getQueue = function(guildId) {
exports.getQueue = function (guildId) {
return djs[guildId].playlist;
};
@ -407,7 +407,7 @@ exports.getQueue = function(guildId) {
* evokes the callback function with the title of the current song
* @param guildId
*/
exports.nowPlaying = function(guildId) {
exports.nowPlaying = function (guildId) {
return djs[guildId].song;
};
@ -415,7 +415,7 @@ exports.nowPlaying = function(guildId) {
* shuffles the queue
* @param guildId
*/
exports.shuffle = function(guildId) {
exports.shuffle = function (guildId) {
djs[guildId].shuffle();
};

@ -0,0 +1,74 @@
/**
* 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