diff --git a/.bash_profile b/.bash_profile new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore index 2897b1b..107071c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea/ node_modules/ npm-debug.log +sh.exe.stackdump \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 41039c0..f0c6bca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: node_js node_js: - '0.10' script: - - npm run jsHint -- --failTaskOnError - echo $BOT >> .env before_install: - export DISPLAY=:99.0 @@ -13,4 +12,5 @@ deploy: provider: modulus api_key: $MODULUS_TOKEN project_name: miscord + on: CommandLogging skip_cleanup: true diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..35cea6c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch node debugging", + "type": "node", + "request": "launch", + "program": "${workspaceRoot}/main.js", + "stopOnEntry": false, + "args": [], + "cwd": "${workspaceRoot}", + "preLaunchTask": null, + "runtimeExecutable": null, + "runtimeArgs": ["--nolazy"], + "env": {"NODE_ENV": "development"}, + "externalConsole": false, + "sourceMaps": false, + "outDir": null + + } + ] +} \ No newline at end of file diff --git a/audio/duh1.mp3 b/audio/random/duh1.mp3 similarity index 100% rename from audio/duh1.mp3 rename to audio/random/duh1.mp3 diff --git a/audio/duh2.mp3 b/audio/random/duh2.mp3 similarity index 100% rename from audio/duh2.mp3 rename to audio/random/duh2.mp3 diff --git a/audio/duh3.mp3 b/audio/random/duh3.mp3 similarity index 100% rename from audio/duh3.mp3 rename to audio/random/duh3.mp3 diff --git a/audio/trump/bigchina.mp3 b/audio/trump/bigchina.mp3 new file mode 100644 index 0000000..160615c Binary files /dev/null and b/audio/trump/bigchina.mp3 differ diff --git a/audio/trump/bomb.mp3 b/audio/trump/bomb.mp3 new file mode 100644 index 0000000..a4d38d1 Binary files /dev/null and b/audio/trump/bomb.mp3 differ diff --git a/audio/trump/china_time.wav b/audio/trump/china_time.wav new file mode 100644 index 0000000..0f6fad0 Binary files /dev/null and b/audio/trump/china_time.wav differ diff --git a/audio/trump/daughter.wav b/audio/trump/daughter.wav new file mode 100644 index 0000000..4f1ffee Binary files /dev/null and b/audio/trump/daughter.wav differ diff --git a/audio/trump/i_run_i_win.mp3 b/audio/trump/i_run_i_win.mp3 new file mode 100644 index 0000000..0ad67ce Binary files /dev/null and b/audio/trump/i_run_i_win.mp3 differ diff --git a/audio/trump/maga.wav b/audio/trump/maga.wav new file mode 100644 index 0000000..c1a3575 Binary files /dev/null and b/audio/trump/maga.wav differ diff --git a/audio/trump/mexico_pays.wav b/audio/trump/mexico_pays.wav new file mode 100644 index 0000000..4f2d818 Binary files /dev/null and b/audio/trump/mexico_pays.wav differ diff --git a/audio/trump/scent.mp3 b/audio/trump/scent.mp3 new file mode 100644 index 0000000..e497eac Binary files /dev/null and b/audio/trump/scent.mp3 differ diff --git a/audio/trump/you'll_get_bored_with_winning.wav b/audio/trump/you'll_get_bored_with_winning.wav new file mode 100644 index 0000000..c53bbb2 Binary files /dev/null and b/audio/trump/you'll_get_bored_with_winning.wav differ diff --git a/db/preference.db b/db/preference.db new file mode 100644 index 0000000..7306476 --- /dev/null +++ b/db/preference.db @@ -0,0 +1,8 @@ +{"command":".gg","path":"./audio/random","volume":"1.2","type":"random","_id":"2ta04BGjEAAP87uA"} +{"command":".reddit","path":null,"volume":null,"type":"reddit","_id":"HTrt2ckQd1B9AXSB"} +{"command":".bag","path":"https://hydra-media.cursecdn.com/dota2.gamepedia.com/e/ee/Wdoc_inthebag_01.mp3","volume":".125","type":"audio","_id":"MFBVfW21GoidA8PF"} +{"command":".cheers","path":"https://my.mixtape.moe/dcvdxs.wav","volume":".5","type":"audio","_id":"O6h1K3ex3nGyGV2x"} +{"command":".gg","path":"./audio/gg.mp3","volume":".25","type":"audio","_id":"bA0ITBRJbRhN9o1P"} +{"command":".lion","path":"http://hydra-media.cursecdn.com/dota2.gamepedia.com/4/4a/Lion_respawn_01.mp3","volume":".25","type":"audio","_id":"bS3gYZIsgNNGSAnQ"} +{"command":".random","path":"./audio/gg.mp3","volume":".25","type":"random","_id":"ht33oaUuP5giHFVm"} +{"command":".trump","path":"./audio/trump","volume":"1.5","type":"random","_id":"ynyIK88s2rwLZ0cZ"} diff --git a/lib/Models/Commands/audioCommand.js b/lib/Models/Commands/audioCommand.js new file mode 100644 index 0000000..97166f4 --- /dev/null +++ b/lib/Models/Commands/audioCommand.js @@ -0,0 +1,20 @@ +'use strict'; + +var BaseCommand = require ('./baseCommand'); +var playFile = require('./../../playFile'); + + +class AudioCommand extends BaseCommand { + + constructor(command, path, volume, bot) { + super(bot, command); + this.path = path; + this.volume = volume; + } + + doWork(voiceChannel) { + playFile(this.bot, voiceChannel, this.volume, this.path); + } +} + +module.exports = AudioCommand; diff --git a/lib/Models/Commands/baseCommand.js b/lib/Models/Commands/baseCommand.js new file mode 100644 index 0000000..8d6e916 --- /dev/null +++ b/lib/Models/Commands/baseCommand.js @@ -0,0 +1,14 @@ +'use strict'; + +class BaseCommand { + constructor(bot, command) { + this.command = command; + this.bot = bot; + } + + doWork() { + //implement in child classes + } +} + +module.exports = BaseCommand; \ No newline at end of file diff --git a/lib/Models/Commands/commandCollection.js b/lib/Models/Commands/commandCollection.js new file mode 100644 index 0000000..a7de3de --- /dev/null +++ b/lib/Models/Commands/commandCollection.js @@ -0,0 +1,41 @@ +'use strict'; + +var command = require('./baseCommand'); + +class CommandCollection { + constructor() { + this.Commands = []; + this.length = function(){ + return this.Commands.length; + }; + } + + Add(command) { + if(this.Commands.indexOf(command) != -1) { + throw new Error("Command already exists in the collection"); + } + this.Commands.push(command); + } + + + Remove(command) { + for(let i = 0; i < this.Commands.length; i ++ ) { + if(Commands[i] === command) { + Commands[i].Remove(); + return; + } + throw new Error("Command not found in collection"); + } + } + + Find(query) { + for(let i = 0; i < this.Commands.length; i++) { + if(this.Commands[i].command === query) { + return this.Commands[i]; + } + throw new Error("Command not found in collection"); + } + } +} + +module.exports = CommandCollection; diff --git a/lib/Models/Commands/randomAudioCommand.js b/lib/Models/Commands/randomAudioCommand.js new file mode 100644 index 0000000..3534539 --- /dev/null +++ b/lib/Models/Commands/randomAudioCommand.js @@ -0,0 +1,22 @@ +'use strict'; +var playFile = require('./../../playFile'); +var utils = require('./../../utils'); + +var BaseCommand = require ('./baseCommand'); + +class RandomAudioCommand extends BaseCommand { + + constructor(command, path, volume, bot) { + super(bot, command); + this.path = path; + this.volume = volume; + this.fileOptions = utils.dirToArray(this.path); + } + + doWork(voiceChannel) { + let chosenFile = this.fileOptions[utils.randomRange(this.fileOptions.length)] + playFile(this.bot, voiceChannel, this.volume, chosenFile); + } +} + +module.exports = RandomAudioCommand; \ No newline at end of file diff --git a/lib/Models/Commands/redditImageCommand.js b/lib/Models/Commands/redditImageCommand.js new file mode 100644 index 0000000..2ca719a --- /dev/null +++ b/lib/Models/Commands/redditImageCommand.js @@ -0,0 +1,40 @@ +'use strict'; + +var parent = require ('./baseCommand'); +var reddit = require('redwrap'); +var LINQ = require('node-linq').LINQ; + +class RedditImageCommand extends BaseCommand { + constructor(command) { + super(command); + } + + doWork(channel, subreddit, msg) { + bot.reply(msg, 'gimme a sec...', function (error, message) { + if (error) { + throw error; + } + console.log(message); + }); + var urlArray = []; + reddit.r(subreddit).limit(100, function (err, data) { + var parsedData = new LINQ(data.data.children) + .Where(function (file) { + if (file.data.url.includes('i.sli.mg') || file.data.url.includes('imgur.com')) { + return true; + } + return false; + }) + .ToArray().forEach(function (item) { + urlArray.push([item.data.url, item.data.title]); + }) + var image = urlArray[utils.randomRange(urlArray.length)]; + bot.sendFile(channel, image[0], null, image[1], function (error, message) { + if (error) { + throw error; + } + console.log(message); + }); + }); + } +} diff --git a/lib/Models/Database/dbContext.js b/lib/Models/Database/dbContext.js new file mode 100644 index 0000000..bdfec87 --- /dev/null +++ b/lib/Models/Database/dbContext.js @@ -0,0 +1,35 @@ +'use strict'; + +var Datastore = require ('nedb'); + +class DbContext { + constructor(dbName) { + this.db = {}; + this.dbPath = './db/' + dbName; + this.db = new Datastore(this.dbPath); + this.db.loadDatabase(); + } + + addEntry(document) { + this.db.insert(document, function(err) { + if(err) { + throw err; + } + }); + } + + getEntry(query) { + return new Promise(function (fulfill, reject) { + this.db.find(query, function (err, docs) { + if (err) { + reject(err); + } + else { + console.log('first' + docs); + fulfill(docs); + } + }); + }); + } +} +module.exports = DbContext; \ No newline at end of file diff --git a/lib/Models/Services/commandService.js b/lib/Models/Services/commandService.js new file mode 100644 index 0000000..7ded40c --- /dev/null +++ b/lib/Models/Services/commandService.js @@ -0,0 +1,24 @@ +'use strict'; + +var dbContext = require('../Database/dbContext'); +var commandCollection = require('../Commands/commandCollection'); +class CommandService { + constructor(db, collection) { + this.db = db; + this.collection = collection; + } + + populateCollection() { + return new Promise(function(resolve) { + resolve({ + value: this.db.getEntry({type: 'audio'}) + }); + }) + } + + populateDatabase() { + + } +} + +module.exports = CommandService; \ No newline at end of file diff --git a/lib/eventLogging.js b/lib/eventLogging.js index e012dac..dac0df2 100644 --- a/lib/eventLogging.js +++ b/lib/eventLogging.js @@ -1,6 +1,7 @@ 'use strict'; var dateFormat = require('dateformat'); +var historyChannel; /** * Records the activities of users joining and leaving voice channels based on the given parameters. @@ -11,13 +12,13 @@ var dateFormat = require('dateformat'); * @param {Object} user the given user */ function history(event, bot, channel, user) { - if (event === 'leave') { - bot.sendMessage(channel.server.channels.get('name', 'history'), '**' + user.username + '**' + ' left **' + channel.name + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**'); + if (event === 'leave' && user.bot === false) { + bot.sendMessage(channel.server.channels.get('name', 'history'), '**' + user.username + '**' + ' left **' + channel.name + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**\n'); } - if (event === 'join') { - bot.sendMessage(channel.server.channels.get('name', 'history'), '**' + user.username + '**' + ' joined **' + channel.name + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**'); - } + if (event === 'join' && user.bot === false) { + bot.sendMessage(channel.server.channels.get('name', 'history'), '**' + user.username + '**' + ' joined **' + channel.name + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**\n'); +} } /** @@ -39,13 +40,13 @@ function presenceUpdated(oldUser, newUser, bot) { } else { statusVerb = 'come'; } - bot.sendMessage(historyChannel, '**' + newUser.username + '**' + ' has ' + statusVerb + ' **' + newUser.status + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**'); + bot.sendMessage(historyChannel, '**' + newUser.username + '**' + ' has ' + statusVerb + ' **' + newUser.status + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**\n'); } if (oldUser.game !== newUser.game) { if (!newUser.game) { - bot.sendMessage(historyChannel, '**' + newUser.username + '**' + ' stopped playing **' + oldUser.game.name + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**'); + bot.sendMessage(historyChannel, '**' + newUser.username + '**' + ' stopped playing **' + oldUser.game.name + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**\n'); } else { - bot.sendMessage(historyChannel, '**' + newUser.username + '**' + ' started playing **' + newUser.game.name + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**'); + bot.sendMessage(historyChannel, '**' + newUser.username + '**' + ' started playing **' + newUser.game.name + '** at **' + dateFormat(new Date(), 'mmmm dS, yyyy, h:MM:ss TT Z') + '**\n'); } } } @@ -54,6 +55,7 @@ function presenceUpdated(oldUser, newUser, bot) { } module.exports = { - history : history, - presenceUpdated : presenceUpdated + history: history, + presenceUpdated: presenceUpdated, + historyChannel: historyChannel }; diff --git a/lib/parser.js b/lib/parser.js index 841755c..7377f51 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1,9 +1,16 @@ 'use strict'; +var reddit = require('redwrap'); +var LINQ = require('node-linq').LINQ; var playFile = require('./playFile'); var utils = require('./utils'); -var historyChannel = require('./eventLogging'); +var random = utils.dirToArray('./audio/random/'); +var tempTrump = utils.dirToArray('./audio/trump/'); +var bot; +var messageChannel; +var commandMap = utils.getMap(); +var validVoiceCommand = true; /** * Parses input from Discord based on the given parameters. * @@ -11,27 +18,93 @@ var historyChannel = require('./eventLogging'); * @param {Object} msg The given message * @param {FastMap} commandMap The given map */ -function parser(bot, msg, commandMap) { - var voiceChannel = msg.author.voiceChannel; - var message = msg.content; - if (!message.startsWith('.')) { +function parser(localBot, msg, commandMap) { + + //only worry about the text if it starts with '.' + if (!msg.content.startsWith('.') || !msg.content.split(' ')[0].startsWith('.')) { return; } - - if (voiceChannel.server.id !== msg.channel.server.id) { - return; + + //ignore the request if the user is in a different server than the server the command was placed in, or the user isn't in a voice channel + if (msg.author.voiceChannel.server.id) { + if (msg.author.voiceChannel.server.id !== msg.channel.server.id) { + validVoiceCommand = false; + } } - if (message === '.help') { + else { + validVoiceCommand = false; + } + + + bot = localBot; + var voiceChannel = msg.author.voiceChannel; + var historyChannel = msg.channel.server.channels.get('name', 'history'); + var message = msg.content.split(' ')[0]; + var param = msg.content.split(' ')[1]; + if(param) { + console.log(param); + } + + //if the command is 'help', or the command isn't found, return available commands + if (message === '.help' || !commandMap.get(message)) { var mapKeys = ''; for (var i = 0; i < commandMap.entries().length; i++) { mapKeys += commandMap.entries()[i][0] + ' '; } bot.reply(msg, 'available commands are: ' + mapKeys); - utils.postMessage(historyChannel.historyChannel, msg.author + ' invoked command \x22.help\x22', bot); + bot.sendMessage(historyChannel, msg.author + ' invoked command \x22.help\x22 \n'); + } + + //if the command type is image + else if (commandMap.get(message)[2] === 'image') { + messageChannel = msg.channel; + eval(commandMap.get(message)[0]); } else { - utils.postMessage(historyChannel.historyChannel, msg.author + ' invoked command \x22' + message + '\x22.', bot); - playFile(bot, voiceChannel, commandMap.get(message)[1], commandMap.get(message)[0]); + bot.sendMessage(historyChannel, msg.author + ' invoked command \x22' + msg.content + '\x22. \n'); + if(validVoiceCommand) { + playFile(bot, voiceChannel, commandMap.get(message)[1], execDecision(message)); + } } } +var execDecision = function (message) { + if (commandMap.get(msg.content)[2] === 'exec') { + return eval(commandMap.get(message)[0]); + } + else { + return (commandMap.get(message)[0]); + } +}; + +//posts a random image from the frontpage of the passed in subreddit +var redditImage = function (channel, subreddit, msg) { + bot.reply(msg, 'gimme a sec...', function(error, message) { + if(error) { + throw error; + } + console.log(message); + }); + var urlArray = []; + reddit.r(subreddit).limit(100, function (err, data) { + var parsedData = new LINQ(data.data.children) + .Where(function (file) { + if (file.data.url.includes('i.sli.mg') || file.data.url.includes('imgur.com')) { + return true; + } + return false; + }) + .ToArray().forEach(function (item) { + urlArray.push([item.data.url, item.data.title]); + }) + var image = urlArray[utils.randomRange(urlArray.length)]; + bot.sendFile(channel, image[0], null , image[1], function(error, message) { + if(error) { + throw error; + } + console.log(message); + }); + }); +}; + + module.exports = parser; diff --git a/lib/playFile.js b/lib/playFile.js index 12a848a..f5b445a 100644 --- a/lib/playFile.js +++ b/lib/playFile.js @@ -1,5 +1,6 @@ 'use strict'; + /** * Plays a file based on the given parameters. * @@ -8,21 +9,25 @@ * @param {String} volume The given volume * @param {String} file The given file to play */ -function playFile(bot, voiceChannel, volume, file) { - bot.joinVoiceChannel(voiceChannel).then(function (voice) { + +var bot; +var localChannel; +function playFile(localBot, voiceChannel, volume, file) { + bot = localBot + localChannel = voiceChannel; + bot.joinVoiceChannel(voiceChannel, function(error, voice) { if (voice.playing) { return; } - voice.playFile(file, {volume : volume}).then(function (intent) { - intent.on('end', function () { - bot.leaveVoiceChannel(voiceChannel); - }); - }).catch(function (err) { - throw err; + voice.playFile(file, { volume: volume }, function(error, intent) { + intent.once('end', endPlayback); }); - }).catch(function (err) { - throw err; }); } +function endPlayback() { + setTimeout(function() { + bot.leaveVoiceChannel(bot.voiceChannel)}, 1500); +} + module.exports = playFile; diff --git a/lib/utils.js b/lib/utils.js index 747f39a..3f61f15 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,9 +1,7 @@ 'use strict'; -var random = ['./audio/duh1.mp3', './audio/duh2.mp3', './audio/duh3.mp3']; -var randomNumber = random.length; var FastMap = require('collections/fast-map'); - +var fs = require('fs'); /** * Gets a random integer between zero to the given parameter. * @@ -21,28 +19,35 @@ function randomRange(range) { */ function getMap() { var localMap = new FastMap(); - localMap.set('.bag', ['https://hydra-media.cursecdn.com/dota2.gamepedia.com/e/ee/Wdoc_inthebag_01.mp3', '.125']); - localMap.set('.gg', ['./audio/gg.mp3', '.25']); - localMap.set('.dp', ['http://hydra-media.cursecdn.com/dota2.gamepedia.com/b/b4/Dpro_wailing_03.mp3', '.125']); - localMap.set('.lion', ['http://hydra-media.cursecdn.com/dota2.gamepedia.com/4/4a/Lion_respawn_01.mp3', '.125']); - localMap.set('.cheers', ['https://my.mixtape.moe/dcvdxs.wav', '.5']); - localMap.set('.random', [random[randomRange(randomNumber)], '1.2']); + localMap.set('.bag', ['https://hydra-media.cursecdn.com/dota2.gamepedia.com/e/ee/Wdoc_inthebag_01.mp3', '.125', 'url']); + localMap.set('.gg', ['./audio/gg.mp3', '.25', 'file']); + localMap.set('.dp', ['http://hydra-media.cursecdn.com/dota2.gamepedia.com/b/b4/Dpro_wailing_03.mp3', '.125', 'url']); + localMap.set('.lion', ['http://hydra-media.cursecdn.com/dota2.gamepedia.com/4/4a/Lion_respawn_01.mp3', '.125', 'url']); + localMap.set('.cheers', ['https://my.mixtape.moe/dcvdxs.wav', '.5', 'file']); + localMap.set('.random', ['random[utils.randomRange(random.length)]', '1.2', 'exec']); + localMap.set('.trump', ['tempTrump[utils.randomRange(tempTrump.length)]', '2.0', 'exec']); + localMap.set('.reddit', ['redditImage(msg.channel, param, msg);', null, 'image']); return localMap; } -/** - * Posts a message to the server. - * - * @param {Object} message The given message - * @param {Object} channel Then given channel - * @param {Object} bot The given bot Client - */ -function postMessage(message, channel, bot) { - bot.sendMessage(channel, message); +function dirToArray(dir) { + var dirArray = []; + fs.readdir(dir, function (err, files) { + if (!err) { + for (var i = 0; i < files.length; i++) { + dirArray.push(dir + files[i]); + } + } + else { + throw err; + } + }); + return dirArray; } + module.exports = { getMap : getMap, randomRange : randomRange, - postMessage : postMessage + dirToArray : dirToArray }; diff --git a/main.js b/main.js index ec839f1..090b6ac 100644 --- a/main.js +++ b/main.js @@ -5,9 +5,26 @@ var eventLogging = require('./lib/eventLogging'); var parser = require('./lib/parser'); var Discord = require('discord.js'); var utils = require('./lib/utils'); - +var DbContext = require('./lib/Models/Database/DbContext'); +var db = new DbContext('preference.db'); var bot = new Discord.Client(); +var commandCollection = require('./lib/Models/Commands/commandCollection'); +var audioCommand = require('./lib/Models/Commands/audioCommand'); +var randomAudioCommand = require('./lib/Models/Commands/randomAudioCommand'); var commandMap = utils.getMap(); +var command = new audioCommand('.gg', 'audio/gg.mp3', '1', bot); +var random = new randomAudioCommand('.random', 'audio/random/', '1', bot); +var collection = new commandCollection(); +var service = require('./lib/Models/Services/commandService'); +collection.Add(command); +collection.Add(random); +collection.Find('.gg').doWork(); +console.log(collection.length()); +var commandService = new service(db, collection); +commandService.populateCollection().then(function(resolve) { + console.log(resolve); +}) + bot.on('voiceJoin', function (channel, user) { eventLogging.history('join', bot, channel, user); diff --git a/package.json b/package.json index 35ce76b..b19e378 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,9 @@ "dateformat": "^1.0.12", "discord.js": "^7.0.1", "dotenv": "^2.0.0", - "node-opus": "^0.1.13" + "node-linq": "0.0.5", + "node-opus": "^0.1.13", + "redwrap": "0.0.4" }, "scripts": { "bot": "node main.js",