From 4f4717a04104380c2d93840116bfb4a417398b65 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Mon, 27 Aug 2012 20:58:56 -0700 Subject: [PATCH 01/29] Removed weather commands --- .gitignore | 3 ++- commands/forecast.js | 35 ----------------------------------- commands/weather.js | 33 --------------------------------- sparkle.js | 6 ++---- 4 files changed, 4 insertions(+), 73 deletions(-) delete mode 100644 commands/forecast.js delete mode 100644 commands/weather.js diff --git a/.gitignore b/.gitignore index 4f77138..92974e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ config.json driver.js node_modules -setup.js \ No newline at end of file +setup.js +.DS_Store \ No newline at end of file diff --git a/commands/forecast.js b/commands/forecast.js deleted file mode 100644 index 43d521d..0000000 --- a/commands/forecast.js +++ /dev/null @@ -1,35 +0,0 @@ -exports.name = '.forecast'; -exports.hidden = false; -exports.enabled = true; -exports.matchStart = true; -exports.handler = function(data) { - var userlocation = data.text.substring(10); - if (userlocation == '') { - userlocation = 20151; - } - - request('http://www.google.com/ig/api?weather=' + encodeURIComponent(userlocation), - function cb(error, response, body) { - parser.parseString(body, function(err, result) { - if (result.weather.forecast_conditions != null) { - try { - var rp = 'Forecast for ' + result.weather.forecast_information.city['@'].data + ': '; - for (i in result.weather.forecast_conditions) { - rp += result.weather.forecast_conditions[i].day_of_week["@"].data - + ': ' - + result.weather.forecast_conditions[i].condition["@"].data - + ' (' + result.weather.forecast_conditions[i].high["@"].data - + '°/' + result.weather.forecast_conditions[i].low["@"].data - + '°). '; - } - output({text: rp, destination: data.source, userid: data.userid}); - } catch (e) { - output({text: 'An error occurred.', destination: data.source, userid: data.userid}); - } - } else { - output({text: 'Sorry, I can\'t find that location.', - destination: data.source, userid: data.userid}); - } - }); - }); -} \ No newline at end of file diff --git a/commands/weather.js b/commands/weather.js deleted file mode 100644 index f3facdd..0000000 --- a/commands/weather.js +++ /dev/null @@ -1,33 +0,0 @@ -exports.name = '.weather'; -exports.hidden = false; -exports.enabled = true; -exports.matchStart = true; -exports.handler = function(data) { - var userlocation = data.text.substring(9); - if (userlocation == '') { - userlocation = 20151; - } - - request('http://www.google.com/ig/api?weather=' + encodeURIComponent(userlocation), - function cb(error, response, body) { - parser.parseString(body, function(err, result) { - if (result.weather.forecast_information != null) { - try { - var rp = 'The weather in ' - + result.weather.forecast_information.city['@'].data - + ' is ' + result.weather.current_conditions.temp_f["@"].data - + '°F and ' + result.weather.current_conditions.condition["@"].data - + ' (' + result.weather.current_conditions.wind_condition["@"].data - + ', ' + result.weather.current_conditions.humidity["@"].data - + ').'; - output({text: rp, destination: data.source, userid: data.userid}); - } catch (e) { - output({text: 'An error occurred.', destination: data.source, userid: data.userid}); - } - } else { - output({text: 'Sorry, I can\'t find that location.', - destination: data.source, userid: data.userid}); - } - }); - }); -} \ No newline at end of file diff --git a/sparkle.js b/sparkle.js index 2caea77..5f99244 100644 --- a/sparkle.js +++ b/sparkle.js @@ -14,7 +14,7 @@ * */ var args = process.argv; -global.version = '[Sparkle] Version 1.0.7'; +global.version = '[Sparkle] Version 1.0.8'; global.fs = require('fs'); global.url = require('url'); @@ -291,16 +291,14 @@ global.setUpDatabase = function() { } global.populateSongData = function(data) { + currentsong = data.room.metadata.current_song; currentsong.artist = data.room.metadata.current_song.metadata.artist; currentsong.song = data.room.metadata.current_song.metadata.song; - currentsong.djname = data.room.metadata.current_song.djname; - currentsong.djid = data.room.metadata.current_song.djid; currentsong.up = data.room.metadata.upvotes; currentsong.down = data.room.metadata.downvotes; currentsong.listeners = data.room.metadata.listeners; currentsong.started = data.room.metadata.current_song.starttime; currentsong.snags = 0; - currentsong.id = data.room.metadata.current_song._id; } //Format: output({text: [required], destination: [required], From fb7d48057bc161e97c823fb3b52b0e31b6547cbc Mon Sep 17 00:00:00 2001 From: Scott Emmons Date: Sun, 7 Oct 2012 12:03:04 -0700 Subject: [PATCH 02/29] Improve table name configuration Externalizes the BANNEDUSERS table name into config.json. Also uses the configured table names and database name consistently now in all places. Prevent the .quake command from crashing the bot if unable to parse earthquake data. --- commands/addban.js | 4 ++-- commands/listbans.js | 8 ++++++-- commands/quake.js | 7 ++++++- commands/stats.js | 6 +++--- commands/unban.js | 4 ++-- config.sample.json | 1 + events.js | 6 +++--- sparkle.js | 10 +++++----- 8 files changed, 28 insertions(+), 18 deletions(-) diff --git a/commands/addban.js b/commands/addban.js index c02e97b..bb8c2a2 100644 --- a/commands/addban.js +++ b/commands/addban.js @@ -18,8 +18,8 @@ exports.handler = function(data) { } function addToBanList(userid, name, bannedby) { - client.query('INSERT INTO BANNED_USERS SET userid = ?, banned_by = ?, timestamp = NOW()', + client.query('INSERT INTO ' + config.database.dbname + '.' + config.database.tablenames.user + ' SET userid = ?, banned_by = ?, timestamp = NOW()', [userid, bannedby]); bot.speak(name + ' (UID ' + userid + ') has been banned by ' + bannedby + '.'); bot.boot(userid, 'You have been banned by ' + bannedby + '.'); -} \ No newline at end of file +} diff --git a/commands/listbans.js b/commands/listbans.js index 9265cbc..7e5613e 100644 --- a/commands/listbans.js +++ b/commands/listbans.js @@ -6,8 +6,12 @@ exports.handler = function(data) { if (config.database.usedb) { client.query('SELECT banned_by, DATE_FORMAT(timestamp, ' + '\'%c/%d/%y\') AS date, username FROM (SELECT * FROM ' - + '`BANNED_USERS`) a INNER JOIN (SELECT * FROM (SELECT *' - + ' FROM USERS ORDER BY lastseen DESC) as test GROUP BY ' + + config.database.dbname + '.' + + config.database.tablenames.banned + ') a INNER JOIN ' + + ' (SELECT * FROM (SELECT *' + + ' FROM ' + config.database.dbname + '.' + + config.database.tablenames.user + + ' ORDER BY lastseen DESC) as test GROUP BY ' + 'userid) b ON a.userid = b.userid', function cb (error, results, fields) { var rp = 'Banned users: '; diff --git a/commands/quake.js b/commands/quake.js index c320817..51cfcb7 100644 --- a/commands/quake.js +++ b/commands/quake.js @@ -8,6 +8,11 @@ exports.handler = function(data) { function (error, response, body) { parser.parseString(body, function (err, result) { var earthquakes = result.entry; + if (earthquakes == null) { + bot.speak('Rock me like a... uhh.. earthquake? Well, that didn\'t work'); + console.log('Unable to parse earthquake info'); + return; + } var rp = 'Recent earthquakes: '; for (var i = 0; i < earthquakes.length && i < 3; i++) { var timeelapsed = new Date() - new Date(earthquakes[i].updated); @@ -19,4 +24,4 @@ exports.handler = function(data) { }); }); } -} \ No newline at end of file +} diff --git a/commands/stats.js b/commands/stats.js index e2205a0..0166945 100644 --- a/commands/stats.js +++ b/commands/stats.js @@ -14,8 +14,8 @@ exports.handler = function(data) { + config.database.dbname + '.' + config.database.tablenames.song + ' group by djid) as djtable'); client.query('SELECT @uniquesongs as uniquesongs, @numdjs as numdjs, ' + 'count(*) as total, sum(up) as up, avg(up) as avgup, ' - + 'sum(down) as down, avg(down) as avgdown FROM ' + config.database.dbname - + '.' + config.database.tablenames.song, + + 'sum(down) as down, avg(down) as avgdown FROM ' + + config.database.dbname + '.' + config.database.tablenames.song, function select(error, results, fields) { var response = ('In this room, ' + results[0]['total'] + ' songs (' @@ -28,4 +28,4 @@ exports.handler = function(data) { output({text: response, destination: data.source, userid: data.userid}); }); } -} \ No newline at end of file +} diff --git a/commands/unban.js b/commands/unban.js index 9889fa4..fa98c05 100644 --- a/commands/unban.js +++ b/commands/unban.js @@ -16,7 +16,7 @@ exports.handler = function(data) { } function removeFromBanList(userid, name, bannedby) { - client.query('DELETE FROM BANNED_USERS WHERE userid = ?', + client.query('DELETE FROM ' + config.database.dbname + '.' + config.database.tablenames.banned + ' WHERE userid = ?', [userid], function (error, results, fields) { if (error == null) { bot.speak(name + ' has been unbanned.'); @@ -25,4 +25,4 @@ function removeFromBanList(userid, name, bannedby) { console.log(error); } }); -} \ No newline at end of file +} diff --git a/config.sample.json b/config.sample.json index 2235e22..62e3512 100644 --- a/config.sample.json +++ b/config.sample.json @@ -12,6 +12,7 @@ {"song": "SONGLIST", "chat": "CHATLOG", "user": "USERS", + "banned": "BANNEDUSERS", "holiday": "HOLIDAY_GREETINGS"}, "login":{ "user":"#####", diff --git a/events.js b/events.js index 0761ef8..2ec5eaf 100755 --- a/events.js +++ b/events.js @@ -153,7 +153,7 @@ exports.registeredEventHandler = function (data) { //See if banned client.query('SELECT userid, banned_by, DATE_FORMAT(timestamp, \'%c/%e/%y\')' - + ' FROM BANNED_USERS WHERE userid LIKE \'' + user.userid + '\'', + + ' FROM ' + config.database.dbname + '.' + config.database.tablenames.banned + ' WHERE userid LIKE \'' + user.userid + '\'', function cb (error, results, fields) { if (results != null && results.length > 0) { bot.boot(user.userid, 'You were banned from this room by ' + results[0]['banned_by'] @@ -463,7 +463,7 @@ exports.pmEventHandler = function(data) { handleCommand(usersList[data.senderid].name, data.senderid, data.text.toLowerCase(), 'pm'); //Case 2: In DB. We have their name. } else if (config.database.usedb) { - client.query('SELECT username FROM `USERS` WHERE userid LIKE \'' + data.senderid + client.query('SELECT username FROM ' + config.database.dbname + '.' + config.database.tablenames.users + ' WHERE userid LIKE \'' + data.senderid + '\' ORDER BY lastseen DESC LIMIT 1', function cb(error, results, fields) { if (results[0] != null) { @@ -540,4 +540,4 @@ exports.httpRequestEventHandler = function(request, response) { var rp = {error: 'Invalid query'}; response.end(JSON.stringify(rp)); } -} \ No newline at end of file +} diff --git a/sparkle.js b/sparkle.js index 5f99244..04a7ac8 100644 --- a/sparkle.js +++ b/sparkle.js @@ -234,7 +234,7 @@ function initializeModules () { //Sets up the database global.setUpDatabase = function() { //song table - client.query('CREATE TABLE ' + config.database.tablenames.song + client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.song + '(id INT(11) AUTO_INCREMENT PRIMARY KEY,' + ' artist VARCHAR(255),' + ' song VARCHAR(255),' @@ -253,7 +253,7 @@ global.setUpDatabase = function() { }); //chat table - client.query('CREATE TABLE ' + config.database.tablenames.chat + client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.chat + '(id INT(11) AUTO_INCREMENT PRIMARY KEY,' + ' userid VARCHAR(255),' + ' chat VARCHAR(255),' @@ -266,7 +266,7 @@ global.setUpDatabase = function() { }); //user table - client.query('CREATE TABLE ' + config.database.tablenames.user + client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.user + '(userid VARCHAR(255), ' + 'username VARCHAR(255), ' + 'lastseen DATETIME, ' @@ -278,8 +278,8 @@ global.setUpDatabase = function() { } }); - client.query('CREATE TABLE BANNED_USERS (' - + 'id INT(11) AUTO_INCREMENT PRIMARY KEY, ' + client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.banned + + '(id INT(11) AUTO_INCREMENT PRIMARY KEY, ' + 'userid VARCHAR(255), ' + 'banned_by VARCHAR(255), ' + 'timestamp DATETIME)', From 6375bebf1648a0904964d4856222cbee44bdc892 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sun, 7 Oct 2012 14:19:37 -0700 Subject: [PATCH 03/29] Fix .quake Updating to read new response format. --- commands/quake.js | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/commands/quake.js b/commands/quake.js index 51cfcb7..4206a09 100644 --- a/commands/quake.js +++ b/commands/quake.js @@ -7,7 +7,7 @@ exports.handler = function(data) { request('http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml', function (error, response, body) { parser.parseString(body, function (err, result) { - var earthquakes = result.entry; + var earthquakes = result.feed.entry; if (earthquakes == null) { bot.speak('Rock me like a... uhh.. earthquake? Well, that didn\'t work'); console.log('Unable to parse earthquake info'); diff --git a/package.json b/package.json index 7ad5bd3..02a4ee9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.0.7", + "version": "1.0.9", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", @@ -25,4 +25,4 @@ }, "devDependencies": {}, "optionalDependencies": {} -} \ No newline at end of file +} From f1c4b7332c7752c75c38642017914198efc10c6c Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sun, 7 Oct 2012 15:04:59 -0700 Subject: [PATCH 04/29] Version checking, enforcement toggle command The version command will now check for the latest version from npm and notify in the response if an update is available. "enforcement [on|off]" now toggles room enforcement. --- commands/enforcementtoggle.js | 23 +++++++++++++++++++++++ commands/version.js | 14 ++++++++++++-- package.json | 2 +- sparkle.js | 2 +- 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 commands/enforcementtoggle.js diff --git a/commands/enforcementtoggle.js b/commands/enforcementtoggle.js new file mode 100644 index 0000000..dfbd7e2 --- /dev/null +++ b/commands/enforcementtoggle.js @@ -0,0 +1,23 @@ +//Toggles the waitlist on/off + +exports.name = '.enforcement'; +exports.hidden = true; +exports.enabled = true; +exports.matchStart = true; +exports.handler = function(data) { + if (admincheck(data.userid)) { + if (data.text.substring(13) == 'on') { + config.enforcement.enforceroom = true; + bot.speak('Room enforcement has been turned on.'); + for (i in djs) { + djs[i].remaining = config.enforcement.songstoplay; + } + } else if (data.text.substring(13) == 'off') { + config.enforcement.enforceroom = false; + bot.speak('Room enforcement has been turned off.'); + for (i in djs) { + djs[i].remaining = config.enforcement.songstoplay - djs[i].remaining; + } + } + } +} \ No newline at end of file diff --git a/commands/version.js b/commands/version.js index 5786900..56a4ec0 100644 --- a/commands/version.js +++ b/commands/version.js @@ -3,6 +3,16 @@ exports.hidden = false; exports.enabled = true; exports.matchStart = false; exports.handler = function(data) { - var response = (version); - output({text: response, destination: data.source, userid: data.userid}); + request('http://registry.npmjs.org/sparklebot/latest', + function (error, response, body) { + var currentversion = JSON.parse(body).version; + var response; + if (package.version != currentversion) { + setTimeout(function() { + output({text: 'Your version of sparklebot is out of date! Update through npm or at http://git.io/meow', destination: data.source, userid: data.userid}); + }, 1000); + } + response = '[Sparkle] ' + package.version + ' (Latest version: ' + currentversion + ')'; + output({text: response, destination: data.source, userid: data.userid}); + }); } \ No newline at end of file diff --git a/package.json b/package.json index 02a4ee9..441ed18 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.0.9", + "version": "1.1.0", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", diff --git a/sparkle.js b/sparkle.js index 04a7ac8..b821eed 100644 --- a/sparkle.js +++ b/sparkle.js @@ -14,7 +14,7 @@ * */ var args = process.argv; -global.version = '[Sparkle] Version 1.0.8'; +global.package = require('./package.json'); global.fs = require('fs'); global.url = require('url'); From 0fed29ea68cfdc84734ed590346fbcda2cc30fc7 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sun, 7 Oct 2012 15:13:03 -0700 Subject: [PATCH 05/29] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 98434b7..297ec9e 100755 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ A customizable Turntable.fm bot written in node.js utilizing [ttapi](https://github.com/alaingilbert/Turntable-API). -This project is no longer in development. If you have new features or bugfixes, feel free to submit a pull request. +This project is still maintained, but I don't have a lot of time for development (so updates will probably be slow). If you have new features or bugfixes, please submit a pull request. ## Installation npm install sparklebot -Check out the new [Get Started guide](https://github.com/sharedferret/Sparkle-Turntable-Bot/wiki/Get-Started) over on the project's [Wiki page](https://github.com/sharedferret/Sparkle-Turntable-Bot/wiki) for detailed installation instructions. +Check out the [Get Started guide](https://github.com/sharedferret/Sparkle-Turntable-Bot/wiki/Get-Started) over on the project's [Wiki page](https://github.com/sharedferret/Sparkle-Turntable-Bot/wiki) for detailed installation instructions. ## Features From 856c6fb0a69d86b8303df97e3c8ea4178c7a3936 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sun, 7 Oct 2012 18:30:40 -0700 Subject: [PATCH 06/29] Small changes/fixes * Added .DS_Store and config/ to .npmignore and .gitignore * Check for null result for db queries * Use config botname in command instead of "meow" * Don't welcome ttstats bots * Small bugfixes Thanks to @ronaldb and @withoutclass for surfacing these bugs/improvements. --- .gitignore | 3 ++- .npmignore | 3 +++ commands/catfact.js | 4 ++-- commands/catfact2.js | 4 ++-- commands/catfact3.js | 4 ++-- commands/catfact4.js | 4 ++-- commands/ohmygodmeow.js | 4 ++-- commands/pilgrim.js | 4 ++-- commands/pilgrim2.js | 4 ++-- commands/pilgrim3.js | 4 ++-- events.js | 2 +- package.json | 2 +- sparkle.js | 6 +++--- 13 files changed, 26 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 92974e9..4319f2a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ config.json driver.js node_modules setup.js -.DS_Store \ No newline at end of file +.DS_Store +config/ \ No newline at end of file diff --git a/.npmignore b/.npmignore index d625de6..7525fc1 100644 --- a/.npmignore +++ b/.npmignore @@ -2,3 +2,6 @@ config.json driver.js node_modules/ setup.js +.DS_Store +config/ + diff --git a/commands/catfact.js b/commands/catfact.js index 23670bd..c646f94 100644 --- a/commands/catfact.js +++ b/commands/catfact.js @@ -6,10 +6,10 @@ exports.handler = function(data) { if (config.database.usedb) { client.query('SELECT * FROM CATFACTS ORDER BY RAND() LIMIT 1', function selectCb(error, results, fields) { - if (results[0] != null) { + if (results != null && results[0] != null) { var response = (results[0]['fact']); output({text: response, destination: data.source, userid: data.userid}); } }); } -} \ No newline at end of file +} diff --git a/commands/catfact2.js b/commands/catfact2.js index 07c3f16..d27406c 100644 --- a/commands/catfact2.js +++ b/commands/catfact2.js @@ -6,10 +6,10 @@ exports.handler = function(data) { if (config.database.usedb) { client.query('SELECT * FROM CATFACTS ORDER BY RAND() LIMIT 1', function selectCb(error, results, fields) { - if (results[0] != null) { + if (results != null && results[0] != null) { var response = (results[0]['fact']); output({text: response, destination: data.source, userid: data.userid}); } }); } -} \ No newline at end of file +} diff --git a/commands/catfact3.js b/commands/catfact3.js index 07c3f16..d27406c 100644 --- a/commands/catfact3.js +++ b/commands/catfact3.js @@ -6,10 +6,10 @@ exports.handler = function(data) { if (config.database.usedb) { client.query('SELECT * FROM CATFACTS ORDER BY RAND() LIMIT 1', function selectCb(error, results, fields) { - if (results[0] != null) { + if (results != null && results[0] != null) { var response = (results[0]['fact']); output({text: response, destination: data.source, userid: data.userid}); } }); } -} \ No newline at end of file +} diff --git a/commands/catfact4.js b/commands/catfact4.js index cb57223..18abfa2 100644 --- a/commands/catfact4.js +++ b/commands/catfact4.js @@ -6,10 +6,10 @@ exports.handler = function(data) { if (config.database.usedb) { client.query('SELECT * FROM CATFACTS ORDER BY RAND() LIMIT 1', function selectCb(error, results, fields) { - if (results[0] != null) { + if (results != null && results[0] != null) { var response = (results[0]['fact']); output({text: response, destination: data.source, userid: data.userid}); } }); } -} \ No newline at end of file +} diff --git a/commands/ohmygodmeow.js b/commands/ohmygodmeow.js index f7ffb63..ba9bb76 100644 --- a/commands/ohmygodmeow.js +++ b/commands/ohmygodmeow.js @@ -1,6 +1,6 @@ //Bot freakout -exports.name = 'oh my god meow'; +exports.name = 'oh my god ' + config.botinfo.botname; exports.hidden = false; exports.enabled = true; exports.matchStart = false; @@ -20,4 +20,4 @@ exports.handler = function(data) { output({text: reptarCall(), destination: data.source, userid: data.userid}); }, 5600); } -} \ No newline at end of file +} diff --git a/commands/pilgrim.js b/commands/pilgrim.js index c7f2978..94ffbe7 100644 --- a/commands/pilgrim.js +++ b/commands/pilgrim.js @@ -6,10 +6,10 @@ exports.handler = function(data) { if (config.database.usedb) { client.query('SELECT * FROM SCOTT_PILGRIM ORDER BY RAND() LIMIT 1', function selectCb(error, results, fields) { - if (results[0] != null) { + if (results != null && results[0] != null) { var response = (results[0]['quote']); output({text: response, destination: data.source, userid: data.userid}); } }); } -} \ No newline at end of file +} diff --git a/commands/pilgrim2.js b/commands/pilgrim2.js index 5a30cc8..b0f51a8 100644 --- a/commands/pilgrim2.js +++ b/commands/pilgrim2.js @@ -6,10 +6,10 @@ exports.handler = function(data) { if (config.database.usedb) { client.query('SELECT * FROM SCOTT_PILGRIM ORDER BY RAND() LIMIT 1', function selectCb(error, results, fields) { - if (results[0] != null) { + if (results != null && results[0] != null) { var response = (results[0]['quote']); output({text: response, destination: data.source, userid: data.userid}); } }); } -} \ No newline at end of file +} diff --git a/commands/pilgrim3.js b/commands/pilgrim3.js index b86a845..bcf1bed 100644 --- a/commands/pilgrim3.js +++ b/commands/pilgrim3.js @@ -6,10 +6,10 @@ exports.handler = function(data) { if (config.database.usedb) { client.query('SELECT * FROM SCOTT_PILGRIM ORDER BY RAND() LIMIT 1', function selectCb(error, results, fields) { - if (results[0] != null) { + if (results != null && results[0] != null) { var response = (results[0]['quote']); output({text: response, destination: data.source, userid: data.userid}); } }); } -} \ No newline at end of file +} diff --git a/events.js b/events.js index 2ec5eaf..8e4280a 100755 --- a/events.js +++ b/events.js @@ -439,7 +439,7 @@ exports.snagEventHandler = function(data) { exports.bootedUserEventHandler = function(data) { //Log in console if (config.consolelog) { - console.log('\u001b[37m\u001b[41m[ Boot ] ' + usersList[data.userid].name + '\u001b[0m'); + console.log('\u001b[37m\u001b[41m[ Boot ] ' + (usersList[data.userid] != null ? usersList[data.userid].name : data.userid) + '\u001b[0m'); } //if the bot was booted, reboot diff --git a/package.json b/package.json index 441ed18..0c02dd8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.1.0", + "version": "1.1.1", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", diff --git a/sparkle.js b/sparkle.js index b821eed..62c4a41 100644 --- a/sparkle.js +++ b/sparkle.js @@ -215,7 +215,7 @@ function initializeModules () { enabled: command.enabled, matchStart: command.matchStart}); } } catch (e) { - // + console.log('Unable to load command: ', e); } //Load http commands @@ -379,8 +379,8 @@ global.addToDb = function (data) { } global.welcomeUser = function (name, id) { - //Ignore ttdashboard bots - if (!name.match(/^ttdashboard/)) { + //Ignore ttstats bots + if (!name.match(/^ttstats/)) { if (id == '4f5628b9a3f7515810008122') { bot.speak(':cat: <3 :wolf:'); } From a9c0643bc15c44eaf331f22e79a4758e0cf114d4 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Tue, 4 Dec 2012 19:32:26 -0800 Subject: [PATCH 07/29] Allow users to actually see their welcome message Add a delay of 1000ms to welcomeUser() to deal with TT not connecting a user to the chatserver until after they join the room. --- events.js | 7 +++++-- package.json | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/events.js b/events.js index 8e4280a..a90c90a 100755 --- a/events.js +++ b/events.js @@ -116,8 +116,11 @@ exports.registeredEventHandler = function (data) { //Greet user //Displays custom greetings for certain members + //Wait for user to join chatserver before welcoming if(config.responses.welcomeusers) { - welcomeUser(user.name, user.userid); + setTimeout(function () { + welcomeUser(user.name, user.userid); + }, 1000); } if (config.responses.welcomepm) { @@ -466,7 +469,7 @@ exports.pmEventHandler = function(data) { client.query('SELECT username FROM ' + config.database.dbname + '.' + config.database.tablenames.users + ' WHERE userid LIKE \'' + data.senderid + '\' ORDER BY lastseen DESC LIMIT 1', function cb(error, results, fields) { - if (results[0] != null) { + if (results != null && results[0] != null) { handleCommand(results[0]['username'], data.senderid, data.text.toLowerCase(), 'pm'); } else { bot.getProfile(data.senderid, function(d) { diff --git a/package.json b/package.json index 0c02dd8..b769b6e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.1.1", + "version": "1.1.2", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", From 96e7888cf706870133476e3a3b2cc1d0b42deea6 Mon Sep 17 00:00:00 2001 From: Taylor Hakes Date: Sun, 9 Dec 2012 11:27:26 -0500 Subject: [PATCH 08/29] -added idle enforcement -added the ability for the bot to dj -added minDjs for song limits -added .snag as alias for addtoplaylist --- .gitignore | 1 + commands/addtoplaylist.js | 14 +- commands/snag.js | 15 + config.sample.json | 141 +++-- events.js | 82 ++- sparkle.js | 1237 +++++++++++++++++++------------------ 6 files changed, 823 insertions(+), 667 deletions(-) create mode 100644 commands/snag.js diff --git a/.gitignore b/.gitignore index 4319f2a..30db447 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ config.json driver.js node_modules setup.js +.idea .DS_Store config/ \ No newline at end of file diff --git a/commands/addtoplaylist.js b/commands/addtoplaylist.js index afabde7..df1f5a6 100644 --- a/commands/addtoplaylist.js +++ b/commands/addtoplaylist.js @@ -5,11 +5,11 @@ exports.hidden = true; exports.enabled = true; exports.matchStart = false; exports.handler = function(data) { - if(admincheck(data.userid)) { - bot.playlistAll(function (data) { - bot.playlistAdd(currentsong.id, data.list.length); - }); - bot.snag(); - output({text: 'Snagging this song, ' + data.name + '!', destination: data.source, userid: data.userid}); - } + if(admincheck(data.userid)) { + bot.snag(); + bot.playlistAll(function (data) { + bot.playlistAdd(currentsong._id, data.list.length); + }); + output({text: 'Snagging this song, ' + data.name + '!', destination: data.source, userid: data.userid}); + } } \ No newline at end of file diff --git a/commands/snag.js b/commands/snag.js new file mode 100644 index 0000000..690db57 --- /dev/null +++ b/commands/snag.js @@ -0,0 +1,15 @@ +//Snags a song to the bot's playlist + +exports.name = '.snag'; +exports.hidden = true; +exports.enabled = true; +exports.matchStart = false; +exports.handler = function(data) { + if(admincheck(data.userid)) { + bot.snag(); + bot.playlistAll(function (data) { + bot.playlistAdd(currentsong._id, data.list.length); + }); + output({text: 'Snagging this song, ' + data.name + '!', destination: data.source, userid: data.userid}); + } +} \ No newline at end of file diff --git a/config.sample.json b/config.sample.json index 62e3512..d5b3d60 100644 --- a/config.sample.json +++ b/config.sample.json @@ -1,59 +1,84 @@ -{"botinfo": - {"auth":"auth+live+#####", - "userid":"#####", - "botname":"meow"}, -"roomid":"4f1e02590c4cc807574030f1", -"admin":"#####", -"database": - {"usedb":true, - "dbname":"nodejs_mysql_treehouse", - "logchat":false, - "tablenames": - {"song": "SONGLIST", - "chat": "CHATLOG", - "user": "USERS", - "banned": "BANNEDUSERS", - "holiday": "HOLIDAY_GREETINGS"}, - "login":{ - "user":"#####", - "password":"#####", - "host": "localhost"}}, -"lastfm": - {"useapi":true, - "lastfmkey":"#####"}, -"http": - {"usehttp": true, - "port": 8181, - "host": "localhost"}, -"responses": - {"respond":true, - "announcebonus":true, - "reportsongstats":true, - "welcomeusers":true, - "welcomepm":true, - "alwayspm":false, - "pmgreet": "Welcome to Indie/Classic Alternative 1 & Done! No queue, fastest finger, play one song and step down. Full rules at http://tinyurl.com/63hr2jl . Type 'commands' to see a list of commands I can respond to.", - "greeting":"Welcome, ", - "ownerresponse":"sharedferret made me!", - "sing":true, - "rules": - {"description": "No queue, fastest finger, play two songs and step down", - "link": "http://tinyurl.com/63hr2jl"} +{"botinfo": { + "auth": "auth+live+#####", + "userid": "#####", + "botname":"meow" +}, + "roomid": "4f1e02590c4cc807574030f1", + "admin": "#####", + "database": { + "usedb": true, + "dbname": "nodejs_mysql_treehouse", + "logchat": false, + "tablenames":{ + "song": "SONGLIST", + "chat": "CHATLOG", + "user": "USERS", + "banned": "BANNEDUSERS", + "holiday":"HOLIDAY_GREETINGS" + }, + "login": { + "user": "#####", + "password":"#####", + "host": "localhost" + } }, -"consolelog":false, -"bonusvote":"VOTE", -"enforcement": - {"enforceroom":true, - "waitlist":false, - "songstoplay":2, - "stepuprules": - {"waittostepup":true, - "waittype":"SONGS", - "length":4}, - "ffarules": - {"multiplespotffa":true, - "timerffa":true, - "timeout":10} - }, -"maintenance": - {"autorejoin": true}} + "lastfm": { + "useapi": true, + "lastfmkey":"#####" + }, + "http": { + "usehttp":true, + "port": 8181, + "host": "localhost" + }, + "responses": { + "respond": true, + "announcebonus": true, + "reportsongstats":true, + "welcomeusers": true, + "welcomepm": true, + "alwayspm": false, + "pmgreet": "Welcome to Indie/Classic Alternative 1 & Done! No queue, fastest finger, play one song and step down. Full rules at http://tinyurl.com/63hr2jl . Type 'commands' to see a list of commands I can respond to.", + "greeting": "Welcome, ", + "ownerresponse": "sharedferret made me!", + "sing": true, + "rules": { + "description":"No queue, fastest finger, play two songs and step down", + "link": "http://tinyurl.com/63hr2jl" + } + }, + "consolelog": false, + "bonusvote": "VOTE", + "djing": { + "botdj": true, + "minDjs":1, + "maxDjs":3 + }, + "enforcement":{ + "enforceroom":true, + "waitlist": false, + "songslimit": { + "limitsongs":true, + "maxsongs": 2, + "minDjs": 5 + }, + "stepuprules":{ + "waittostepup":true, + "waittype": "SONGS", + "length": 4 + }, + "ffarules": { + "multiplespotffa":true, + "timerffa": true, + "timeout": 10 + }, + "idle": { + "idlerules": true, + "idlewarntime": 10, + "idleremovaltime":12, + "minDjs": 5 + } + }, + "maintenance":{ + "autorejoin":true + }} diff --git a/events.js b/events.js index a90c90a..ed91c7a 100755 --- a/events.js +++ b/events.js @@ -2,6 +2,10 @@ exports.readyEventHandler = function (data) { if (config.database.usedb) { setUpDatabase(); } + + loop(); + + } //Runs when the room is changed. @@ -78,6 +82,21 @@ exports.updateVoteEventHandler = function (data) { } } + // Update the last activity for the dj if it was sending the message and remove the warning + if(config.enforcement.enforceroom && config.enforcement.idle.idlerules) { + var votes = data.room.metadata.votelog; + for(var i = 0; i < votes.length; i++) { + var vote = votes[i]; + var userid = vote[0]; + for(var i in djs) { + if(djs[i].id == userid) { + djs[i].lastActivity = new Date(); + djs[i].warned = false; + } + } + } + } + //Log vote in console //Note: Username only displayed for upvotes, since TT doesn't broadcast // username for downvote events. @@ -218,6 +237,16 @@ exports.speakEventHandler = function (data) { if (config.responses.respond) { handleCommand(data.name, data.userid, data.text.toLowerCase(), 'speak'); } + + // Update the last activity for the dj if it was sending the message and remove the warning + if(config.enforcement.enforceroom && config.enforcement.idle.idlerules) { + for(var i in djs) { + if(djs[i].id == data.userid) { + djs[i].lastActivity = new Date(); + djs[i].warned = false; + } + } + } } exports.noSongEventHandler = function(data) { @@ -234,8 +263,8 @@ exports.endSongEventHandler = function (data) { //If a DJ that needed to step down hasn't by the end of the //next DJ's song, remove them immediately - if (config.enforcement.enforceroom && !userstepped) { - bot.remDj(usertostep); + if (config.enforcement.enforceroom && usertostep && !userstepped) { + bot.remDj(usertostep); } //Used for room enforcement @@ -275,9 +304,7 @@ exports.newSongEventHandler = function (data) { //Enforce stepdown rules if (usertostep != null) { - if (usertostep == config.botinfo.userid) { - bot.remDj(config.botinfo.userid); - } else if (config.enforcement.enforceroom) { + if (config.enforcement.enforceroom) { enforceRoom(); } } @@ -370,6 +397,12 @@ exports.remDjEventHandler = function (data) { djs.splice(i, 1); } } + + // Check if the bot was removed + if(isBot(data.user[0].userid)) { + isdjing = false; + } + //If more than one DJ spot is open, set free-for-all mode to true if (config.enforcement.enforceroom && config.enforcement.ffarules.multiplespotffa) { @@ -392,17 +425,23 @@ exports.addDjEventHandler = function(data) { //Add to DJ list if (config.enforcement.enforceroom) { - var toplay = config.enforcement.songstoplay; - //If they've been up recently, modify their remaining count - for (i in partialdjs) { - if (partialdjs[i].id == data.user[0].userid) { - toplay = partialdjs[i].lefttoplay; - partialdjs.splice(i, 1); - } - } - djs.push({id: data.user[0].userid, remaining: toplay}); + var toplay = Infinity; + + if(config.enforcement.songslimit.limitsongs) { + toplay = config.enforcement.songslimit.maxsongs; + + //If they've been up recently, modify their remaining count + for (i in partialdjs) { + if (partialdjs[i].id == data.user[0].userid) { + toplay = partialdjs[i].lefttoplay; + partialdjs.splice(i, 1); + } + } + } + + djs.push({id: data.user[0].userid, remaining: toplay, lastActivity: new Date(), user: data.user[0] }); } else { - djs.push({id: data.user[0].userid, remaining: 0}); + djs.push({id: data.user[0].userid, remaining: Infinity}); } @@ -460,7 +499,18 @@ exports.bootedUserEventHandler = function(data) { } exports.pmEventHandler = function(data) { - try { + + // Update the last activity for the dj if it was sending the message and remove the warning + if(config.enforcement.enforceroom && config.enforcement.idle.idlerules) { + for(var i in djs) { + if(djs[i].id == data.senderid) { + djs[i].lastActivity = new Date(); + djs[i].warned = false; + } + } + } + + try { //Case 1: In room. We have their name. if (usersList[data.senderid] != null) { handleCommand(usersList[data.senderid].name, data.senderid, data.text.toLowerCase(), 'pm'); diff --git a/sparkle.js b/sparkle.js index 62c4a41..e9aa51b 100644 --- a/sparkle.js +++ b/sparkle.js @@ -1,23 +1,23 @@ /** * sparkle.js * Author: sharedferret - * + * * A Turntable.fm bot for the Indie/Classic Alternative 1 + Done room. * Based on bot implementations by anamorphism and heatvision * Uses node.js with node modules ttapi, mysql, request - * + * * Run: 'node sparkle.js' - * + * * Make sure parameters in config.js are set before running. * Make sure a mysql server instance is running before starting the bot (if useDatabase * is enabled in the config file) * -*/ + */ var args = process.argv; global.package = require('./package.json'); global.fs = require('fs'); -global.url = require('url'); +global.url = require('url'); global.Bot; global.bot; @@ -38,7 +38,7 @@ initializeModules(); global.usersList = { }; //A list of users in the room global.djs = new Array(); //A list of current DJs global.partialdjs = new Array(); //A list of DJs who have stepped down before their - //allotted # of songs +//allotted # of songs //Room enforcement variables global.usertostep = null; //The userid of the DJ to step down @@ -47,6 +47,7 @@ global.enforcementtimeout = new Date();//The time that the user stepped down global.ffa = false; //A flag denoting if free-for-all mode is active global.legalstepdown = true; //A flag denoting if a user stepped up legally global.pastdjs = new Array(); //An array of the past 4 DJs +global.isdjing = false; global.waitlist = new Array(); global.moderators = new Array(); @@ -57,16 +58,16 @@ global.bonusvotepoints = 0; //The number of awesomes needed for the b //Current song info global.currentsong = { - artist: null, - song: null, - djname: null, - djid: null, - up: 0, - down: 0, - listeners: 0, - snags: 0, - id: null }; - + artist: null, + song: null, + djname: null, + djid: null, + up: 0, + down: 0, + listeners:0, + snags: 0, + id: null }; + // Event listeners bot.on('ready', events.readyEventHandler); @@ -95,7 +96,7 @@ bot.on('snagged', events.snagEventHandler); bot.on('booted_user', events.bootedUserEventHandler); -bot.on('pmmed', events.pmEventHandler); +bot.on('pmmed', events.pmEventHandler); bot.on('update_user', events.updateUserEventHandler); @@ -106,638 +107,702 @@ bot.on('rem_moderator', events.removeModeratorEventHandler); bot.on('httpRequest', events.httpRequestEventHandler); process.on('message', function(data) { - if (data.deliverCommand != null) { - bot.speak(data.deliverCommand); - } + if(data.deliverCommand != null) { + bot.speak(data.deliverCommand); + } }); // Functions -function initializeModules () { - //Creates the bot listener - try { - Bot = require('ttapi'); - } catch(e) { - console.log(e); - console.log('It is likely that you do not have the ttapi node module installed.' - + '\nUse the command \'npm install ttapi\' to install.'); - process.exit(33); - } - - //Creates the config object - try { - if (args[2] == '-c' && args[3] != null) { - config = JSON.parse(fs.readFileSync(args[3], 'ascii')); - } else { - config = JSON.parse(fs.readFileSync('config.json', 'ascii')); - } - } catch(e) { - //todo: update error handling - console.log(e); - console.log('Error loading config.json. Check that your config file exists and is valid JSON.'); - process.exit(33); - } - - bot = new Bot(config.botinfo.auth, config.botinfo.userid, config.roomid); - - //Loads bot singalongs - if (config.responses.sing) { - try { - singalong = require('./singalong.js'); - } catch (e) { - console.log(e); - console.log('Ensure that singalong.js is present in this directory,' - + ' or disable the botSing flag in config.js'); - console.log('Starting bot without singalong functionality.'); - config.responses.sing = false; - } - } - - //Creates mysql db object - if (config.database.usedb) { - try { - mysql = require('mysql'); - } catch(e) { - console.log(e); - console.log('It is likely that you do not have the mysql node module installed.' - + '\nUse the command \'npm install mysql\' to install.'); - console.log('Starting bot without database functionality.'); - config.database.usedb = false; - } - - //Connects to mysql server - try { - var dbhost = 'localhost'; - if (config.database.login.host != null && config.database.login.host != '') { - dbhost = config.database.login.host; - } - client = mysql.createClient({user: config.database.login.user, password: config.database.login.password, database: config.database.dbname, host: dbhost}); - } catch(e) { - console.log(e); - console.log('Make sure that a mysql server instance is running and that the ' - + 'username and password information in config.js are correct.'); - console.log('Starting bot without database functionality.'); - config.database.usedb = false; - } - } - - //Initializes request module - try { - request = require('request'); - } catch(e) { - console.log(e); - console.log('It is likely that you do not have the request node module installed.' - + '\nUse the command \'npm install request\' to install.'); - process.exit(33); - } - - try { - xml2js = require('xml2js'); - parser = new xml2js.Parser(); - } catch(e) { - console.log(e); - console.log('It is likely that you do not have the xml2js node module installed.' - + '\nUse the command \'npm install xml2js\' to install.'); - process.exit(33); - } - - //Create HTTP listeners - if (config.http.usehttp) { - bot.listen(config.http.port, config.http.host); - } - - //Load commands - try { - var filenames = fs.readdirSync('./commands'); - for (i in filenames) { - var command = require('./commands/' + filenames[i]); - commands.push({name: command.name, handler: command.handler, hidden: command.hidden, - enabled: command.enabled, matchStart: command.matchStart}); - } - } catch (e) { - console.log('Unable to load command: ', e); - } - - //Load http commands - try { - var filenames = fs.readdirSync('./api'); - for (i in filenames) { - var command = require('./api/' + filenames[i]); - httpcommands.push({name: command.name, handler: command.handler, hidden: command.hidden, - enabled: command.enabled}); - } - } catch (e) { - // - } +function initializeModules() { + //Creates the bot listener + try { + Bot = require('ttapi'); + } catch(e) { + console.log(e); + console.log('It is likely that you do not have the ttapi node module installed.' + + '\nUse the command \'npm install ttapi\' to install.'); + process.exit(33); + } + + //Creates the config object + try { + if(args[2] == '-c' && args[3] != null) { + config = JSON.parse(fs.readFileSync(args[3], 'ascii')); + } else { + config = JSON.parse(fs.readFileSync('config.json', 'ascii')); + } + } catch(e) { + //todo: update error handling + console.log(e); + console.log('Error loading config.json. Check that your config file exists and is valid JSON.'); + process.exit(33); + } + + bot = new Bot(config.botinfo.auth, config.botinfo.userid, config.roomid); + + //Loads bot singalongs + if(config.responses.sing) { + try { + singalong = require('./singalong.js'); + } catch(e) { + console.log(e); + console.log('Ensure that singalong.js is present in this directory,' + + ' or disable the botSing flag in config.js'); + console.log('Starting bot without singalong functionality.'); + config.responses.sing = false; + } + } + + //Creates mysql db object + if(config.database.usedb) { + try { + mysql = require('mysql'); + } catch(e) { + console.log(e); + console.log('It is likely that you do not have the mysql node module installed.' + + '\nUse the command \'npm install mysql\' to install.'); + console.log('Starting bot without database functionality.'); + config.database.usedb = false; + } + + //Connects to mysql server + try { + var dbhost = 'localhost'; + if(config.database.login.host != null && config.database.login.host != '') { + dbhost = config.database.login.host; + } + client = + mysql.createClient({user:config.database.login.user, password:config.database.login.password, database:config.database.dbname, host:dbhost}); + } catch(e) { + console.log(e); + console.log('Make sure that a mysql server instance is running and that the ' + + 'username and password information in config.js are correct.'); + console.log('Starting bot without database functionality.'); + config.database.usedb = false; + } + } + + //Initializes request module + try { + request = require('request'); + } catch(e) { + console.log(e); + console.log('It is likely that you do not have the request node module installed.' + + '\nUse the command \'npm install request\' to install.'); + process.exit(33); + } + + try { + xml2js = require('xml2js'); + parser = new xml2js.Parser(); + } catch(e) { + console.log(e); + console.log('It is likely that you do not have the xml2js node module installed.' + + '\nUse the command \'npm install xml2js\' to install.'); + process.exit(33); + } + + //Create HTTP listeners + if(config.http.usehttp) { + bot.listen(config.http.port, config.http.host); + } + + //Load commands + try { + var filenames = fs.readdirSync('./commands'); + for(i in filenames) { + var command = require('./commands/' + filenames[i]); + commands.push({name:command.name, handler:command.handler, hidden:command.hidden, + enabled: command.enabled, matchStart:command.matchStart}); + } + } catch(e) { + console.log('Unable to load command: ', e); + } + + //Load http commands + try { + var filenames = fs.readdirSync('./api'); + for(i in filenames) { + var command = require('./api/' + filenames[i]); + httpcommands.push({name:command.name, handler:command.handler, hidden:command.hidden, + enabled: command.enabled}); + } + } catch(e) { + // + } + } //Sets up the database global.setUpDatabase = function() { - //song table - client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.song - + '(id INT(11) AUTO_INCREMENT PRIMARY KEY,' - + ' artist VARCHAR(255),' - + ' song VARCHAR(255),' - + ' djid VARCHAR(255),' - + ' up INT(3),' + ' down INT(3),' - + ' listeners INT(3),' - + ' started DATETIME,' - + ' snags INT(3),' - + ' bonus INT(3))', - - function (error) { - //Handle an error if it's not a table already exists error - if(error && error.number != 1050) { - throw (error); - } - }); - - //chat table - client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.chat - + '(id INT(11) AUTO_INCREMENT PRIMARY KEY,' - + ' userid VARCHAR(255),' - + ' chat VARCHAR(255),' - + ' time DATETIME)', - function (error) { - //Handle an error if it's not a table already exists error - if(error && error.number != 1050) { - throw (error); - } - }); - - //user table - client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.user - + '(userid VARCHAR(255), ' - + 'username VARCHAR(255), ' - + 'lastseen DATETIME, ' - + 'PRIMARY KEY (userid, username))', - function (error) { - //Handle an error if it's not a table already exists error - if(error && error.number != 1050) { - throw (error); - } - }); - - client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.banned - + '(id INT(11) AUTO_INCREMENT PRIMARY KEY, ' - + 'userid VARCHAR(255), ' - + 'banned_by VARCHAR(255), ' - + 'timestamp DATETIME)', - function (error) { - if (error && error.number != 1050) { - throw error; - } - }); + //song table + client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.song + + '(id INT(11) AUTO_INCREMENT PRIMARY KEY,' + + ' artist VARCHAR(255),' + + ' song VARCHAR(255),' + + ' djid VARCHAR(255),' + + ' songid VARCHAR(255),' + + ' up INT(3),' + ' down INT(3),' + + ' listeners INT(3),' + + ' started DATETIME,' + + ' snags INT(3),' + + ' bonus INT(3))', + + function(error) { + //Handle an error if it's not a table already exists error + if(error && error.number != 1050) { + throw (error); + } + }); + + //chat table + client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.chat + + '(id INT(11) AUTO_INCREMENT PRIMARY KEY,' + + ' userid VARCHAR(255),' + + ' chat VARCHAR(255),' + + ' time DATETIME)', + function(error) { + //Handle an error if it's not a table already exists error + if(error && error.number != 1050) { + throw (error); + } + }); + + //user table + client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.user + + '(userid VARCHAR(255), ' + + 'username VARCHAR(255), ' + + 'lastseen DATETIME, ' + + 'PRIMARY KEY (userid, username))', + function(error) { + //Handle an error if it's not a table already exists error + if(error && error.number != 1050) { + throw (error); + } + }); + + client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.banned + + '(id INT(11) AUTO_INCREMENT PRIMARY KEY, ' + + 'userid VARCHAR(255), ' + + 'banned_by VARCHAR(255), ' + + 'timestamp DATETIME)', + function(error) { + if(error && error.number != 1050) { + throw error; + } + }); } global.populateSongData = function(data) { - currentsong = data.room.metadata.current_song; - currentsong.artist = data.room.metadata.current_song.metadata.artist; - currentsong.song = data.room.metadata.current_song.metadata.song; - currentsong.up = data.room.metadata.upvotes; - currentsong.down = data.room.metadata.downvotes; - currentsong.listeners = data.room.metadata.listeners; - currentsong.started = data.room.metadata.current_song.starttime; - currentsong.snags = 0; + currentsong = data.room.metadata.current_song; + currentsong.artist = data.room.metadata.current_song.metadata.artist; + currentsong.song = data.room.metadata.current_song.metadata.song; + currentsong.up = data.room.metadata.upvotes; + currentsong.down = data.room.metadata.downvotes; + currentsong.listeners = data.room.metadata.listeners; + currentsong.started = data.room.metadata.current_song.starttime; + currentsong.snags = 0; } //Format: output({text: [required], destination: [required], // userid: [required for PM], format: [optional]}); -global.output = function (data) { - if (data.destination == 'speak') { - bot.speak(data.text); - } else if (data.destination == 'pm') { - bot.pm(data.text, data.userid); - } else if (data.destination == 'http') { - response.writeHead(200, {'Content-Type': 'text/plain'}); - if (data.format == 'json') { - response.end(JSON.stringify(data.text)); - } else { - response.end(data.text); - } - } +global.output = function(data) { + if(data.destination == 'speak') { + bot.speak(data.text); + } else if(data.destination == 'pm') { + bot.pm(data.text, data.userid); + } else if(data.destination == 'http') { + response.writeHead(200, {'Content-Type':'text/plain'}); + if(data.format == 'json') { + response.end(JSON.stringify(data.text)); + } else { + response.end(data.text); + } + } } //Checks if the user id is present in the admin list. Authentication //for admin-only privileges. -global.admincheck = function (userid) { - return (userid === config.admin || - moderators.some(function(moderatorid) { - return moderatorid === userid; - })); +global.admincheck = function(userid) { + return (userid === config.admin || + moderators.some(function(moderatorid) { + return moderatorid === userid; + })); +} + +global.loop = function() { + setInterval(function() { + if(config.enforcement.enforceroom && config.enforcement.idle.idlerules) { + checkAFK(); + } + + if(config.djing.botdj) { + checkDjs(); + } + }, 5000); } //TODO: Implement -global.checkAuth = function (givenKey) { - return false; +global.checkAuth = function(givenKey) { + return false; } -global.checkAFK = function() { - // +global.isBot = function(id) { + return id == config.botinfo.userid; } +global.checkAFK = function() { + + if(djs.length >= config.enforcement.idle.minDjs) { + for(i in djs) { + if(!isBot(djs[i].id) && (new Date()) - djs[i].lastActivity > 1000 * 60 * config.enforcement.idle.idlewarntime) { + // If they were already warned don't do it again + if(djs[i].warned) { + return; + } + + // Warn the DJ and set timer to remove them + djs[i].warned = true; + bot.speak('@' + djs[i].user.name + ', you have been idle for ' + config.enforcement.idle.idlewarntime + + ' minutes. Please Awesome or speak in console to remain dj.'); + (function(id) { + setTimeout(function() { + for(j in djs) { + if(id == djs[j].id && djs[j].warned) { + bot.speak('@' + djs[i].user.name + ', you have been idle for ' + + config.enforcement.idle.idleremovaltime + ' minutes. You are no longer DJ.'); + bot.remDj(djs[i].id); + } + } + }, 1000 * 60 * (config.enforcement.idle.idleremovaltime - config.enforcement.idle.idlewarntime)); + })(djs[i].id); + } + } + } +}; + +global.checkDjs = function() { + var extra = isdjing ? -1 : 0; + if(djs.length + extra >= config.djing.minDjs && djs.length + extra <= config.djing.maxDjs) { + if(isdjing) { + return; + } + isdjing = true; + bot.addDj(); + } else if(isdjing) { + isdjing = false; + bot.remDj(); + } +}; + //The bot will respond to a Reptar call with a variant of 'rawr!' based on //the result from a RNG. -global.reptarCall = function (source) { - var rand = Math.random(); - var response = ''; - if (rand < 0.05) { - response = ('That band is pretty awesome.'); - } else if (rand < 0.10) { - response = ('Good morning!'); - } else if (rand < 0.17) { - response = ('Rawr!'); - } else if (rand < 0.3) { - response = ('rawr!'); - } else if (rand < 0.4) { - response = ('RAWR!'); - } else if (rand < 0.5) { - response = ('rawr.'); - } else if (rand < 0.6) { - response = ('RAWR!!!'); - } else { - response = ('.reptar'); - } - return response; +global.reptarCall = function(source) { + var rand = Math.random(); + var response = ''; + if(rand < 0.05) { + response = ('That band is pretty awesome.'); + } else if(rand < 0.10) { + response = ('Good morning!'); + } else if(rand < 0.17) { + response = ('Rawr!'); + } else if(rand < 0.3) { + response = ('rawr!'); + } else if(rand < 0.4) { + response = ('RAWR!'); + } else if(rand < 0.5) { + response = ('rawr.'); + } else if(rand < 0.6) { + response = ('RAWR!!!'); + } else { + response = ('.reptar'); + } + return response; } //Adds the song data to the songdata table. //This runs on the endsong event. -global.addToDb = function (data) { - client.query( - 'INSERT INTO ' + config.database.dbname + '.' + config.database.tablenames.song +' ' - + 'SET artist = ?,song = ?, djid = ?, up = ?, down = ?,' - + 'listeners = ?, started = NOW(), snags = ?, bonus = ?', - [currentsong.artist, - currentsong.song, - currentsong.djid, - currentsong.up, - currentsong.down, - currentsong.listeners, - currentsong.snags, - bonuspoints.length]); +global.addToDb = function(data) { + client.query( + 'INSERT INTO ' + config.database.dbname + '.' + config.database.tablenames.song + ' ' + + 'SET artist = ?,song = ?, songid = ?, djid = ?, up = ?, down = ?,' + + 'listeners = ?, started = NOW(), snags = ?, bonus = ?', + [currentsong.artist, + currentsong.song, + currentsong._id, + currentsong.djid, + currentsong.up, + currentsong.down, + currentsong.listeners, + currentsong.snags, + bonuspoints.length]); } -global.welcomeUser = function (name, id) { - //Ignore ttstats bots - if (!name.match(/^ttstats/)) { - if (id == '4f5628b9a3f7515810008122') { - bot.speak(':cat: <3 :wolf:'); - } - else if (id == '4df0443f4fe7d0631905d6a8') { - bot.speak(':cat: <3 ' + name); - } - else if (config.database.usedb) { - client.query('SELECT greeting FROM ' + config.database.dbname + '.' - + config.database.tablenames.holiday + ' WHERE date LIKE CURDATE()', - function cbfunc(error, results, fields) { - if (results != null && results[0] != null) { - bot.speak(results[0]['greeting'] + ', ' + name + '!'); - } else { - bot.speak(config.responses.greeting + name + '!'); - } - }); - } else { - bot.speak(config.responses.greeting + name + '!'); - } - } +global.welcomeUser = function(name, id) { + //Ignore ttstats bots + if(!name.match(/^ttstats/)) { + if(id == '4f5628b9a3f7515810008122') { + bot.speak(':cat: <3 :wolf:'); + } + else if(id == '4df0443f4fe7d0631905d6a8') { + bot.speak(':cat: <3 ' + name); + } + else if(config.database.usedb) { + client.query('SELECT greeting FROM ' + config.database.dbname + '.' + + config.database.tablenames.holiday + ' WHERE date LIKE CURDATE()', + function cbfunc(error, results, fields) { + if(results != null && results[0] != null) { + bot.speak(results[0]['greeting'] + ', ' + name + '!'); + } else { + bot.speak(config.responses.greeting + name + '!'); + } + }); + } else { + bot.speak(config.responses.greeting + name + '!'); + } + } } //Reminds a user that has just played a song to step down, and pulls them //off stage if they do not step down. -global.enforceRoom = function () { - setTimeout( function() { - if(!userstepped) { - bot.speak('@' + usersList[usertostep].name + ', please step down'); - setTimeout( function() { - if(!userstepped) { - bot.remDj(usertostep); - } - }, 15000); - } - }, 15000); +global.enforceRoom = function() { + setTimeout(function() { + if(!userstepped) { + bot.speak('@' + usersList[usertostep].name + ', please step down'); + setTimeout(function() { + if(!userstepped) { + bot.remDj(usertostep); + } + }, 15000); + } + }, 15000); } -global.reducePastDJCounts = function (djid) { - //First, decrement last DJ count by 1. Set to remove if they need to step down - for (i in djs) { - if (djs[i].id == djid) { - djs[i].remaining--; - if (djs[i].remaining <= 0) { - userstepped = false; - usertostep = djid; - } - } - } - - //Reduces past DJ counts and removes from past dj list if necessary - if (config.enforcement.stepuprules.waittostepup) { - - //Decrease count in pastdjs list by 1 - if (config.enforcement.stepuprules.waittype == 'SONGS') { - for (i in pastdjs) { - pastdjs[i].wait--; - } - - //Remove if they're done waiting - for (i in pastdjs) { - if (pastdjs[i].wait < 1) { - pastdjs.splice(i, 1); - } - } - } - else if (config.enforcement.stepuprules.waittype == 'MINUTES') { - //tbh nothing should be here - } - } +global.reducePastDJCounts = function(djid) { + //First, decrement last DJ count by 1. Set to remove if they need to step down + + if(config.enforcement.songslimit.limitsongs && djs.length >= config.enforcement.songslimit.minDjs) { + for(i in djs) { + if(djs[i].id == djid) { + djs[i].remaining--; + if(djs[i].remaining <= 0) { + userstepped = false; + usertostep = djid; + } + } + } + } + + + //Reduces past DJ counts and removes from past dj list if necessary + if(config.enforcement.stepuprules.waittostepup) { + + //Decrease count in pastdjs list by 1 + if(config.enforcement.stepuprules.waittype == 'SONGS') { + for(i in pastdjs) { + pastdjs[i].wait--; + } + + //Remove if they're done waiting + for(i in pastdjs) { + if(pastdjs[i].wait < 1) { + pastdjs.splice(i, 1); + } + } + } + else if(config.enforcement.stepuprules.waittype == 'MINUTES') { + //tbh nothing should be here + } + } } //Adds the user to the past DJ list -global.addToPastDJList = function (userid) { - if (config.enforcement.stepuprules.waittype == 'SONGS') { - pastdjs.push({id: userid, wait: config.enforcement.stepuprules.length}); - } - else if (config.enforcement.stepuprules.waittype == 'MINUTES') { - var pushdate = new Date(); - pastdjs.push({id: userid, wait: pushdate}); - - //I don't think this works yet, but it's how i should remove people - var fnc = function(y) { - setTimeout(function() { - for (i in pastdjs) { - if ((new Date().getTime() - pastdjs[i].wait.getTime()) > - (config.enforcement.stepuprules.length * 60000) - && (pushdate == pastdjs[i].wait)) { - pastdjs.splice(i, 1); - } - } - }, config.enforcement.stepuprules.length * 60000); - }(pushdate); - } +global.addToPastDJList = function(userid) { + if(config.enforcement.stepuprules.waittype == 'SONGS') { + pastdjs.push({id:userid, wait:config.enforcement.stepuprules.length}); + } + else if(config.enforcement.stepuprules.waittype == 'MINUTES') { + var pushdate = new Date(); + pastdjs.push({id:userid, wait:pushdate}); + + //I don't think this works yet, but it's how i should remove people + var fnc = function(y) { + setTimeout(function() { + for(i in pastdjs) { + if((new Date().getTime() - pastdjs[i].wait.getTime()) > + (config.enforcement.stepuprules.length * 60000) + && (pushdate == pastdjs[i].wait)) { + pastdjs.splice(i, 1); + } + } + }, config.enforcement.stepuprules.length * 60000); + }(pushdate); + } } -global.addToWaitlist = function (userid, name, source) { - //Case 1: User is DJing already - for (i in djs) { - if (djs[i].id == userid) { - output({text: 'You\'re currently DJing!', destination: source, userid: userid}); - return false; - } - } - - //Case 2: User is already in the waitlist - for (i in waitlist) { - if (waitlist[i].id == userid) { - output({text: 'You\'re already on the list, ' + name + '.', destination: source, - userid: userid}); - return false; - } - } - - //Otherwise, add to waitlist - waitlist.push({name: name, id: userid}); - output({text: 'You\'ve been added to the queue. Your position is ' + waitlist.length + '.', - destination: source, userid: userid}); - if (waitlist.length == 1 && djs.length < 5) { - announceNextPersonOnWaitlist(); - } - return true; +global.addToWaitlist = function(userid, name, source) { + //Case 1: User is DJing already + for(i in djs) { + if(djs[i].id == userid) { + output({text:'You\'re currently DJing!', destination:source, userid:userid}); + return false; + } + } + + //Case 2: User is already in the waitlist + for(i in waitlist) { + if(waitlist[i].id == userid) { + output({text:'You\'re already on the list, ' + name + '.', destination:source, + userid: userid}); + return false; + } + } + + //Otherwise, add to waitlist + waitlist.push({name:name, id:userid}); + output({text: 'You\'ve been added to the queue. Your position is ' + waitlist.length + '.', + destination:source, userid:userid}); + if(waitlist.length == 1 && djs.length < 5) { + announceNextPersonOnWaitlist(); + } + return true; } -global.checkStepup = function (userid, name) { - //Get time elapsed between previous dj stepping down and this dj stepping up - var waittime = new Date().getTime() - enforcementtimeout.getTime(); - for (i in pastdjs) { - if (pastdjs[i].id == userid) { - //if the user waited longer than the FFA timeout or it's a free-for-all, - //remove from list. Else, remove dj and warn - - if (config.enforcement.ffarules.multiplespotffa && ffa) { - legalstepdown = true; - } - else if (config.enforcement.ffarules.timerffa) { - legalstepdown = (waittime > (config.enforcement.ffarules.timeout * 1000)); - } - else { - legalstepdown = false; - } - - if (legalstepdown) { - for (i in pastdjs) { - if (pastdjs[i].id == userid) { - pastdjs.splice(i, 1); - } - } - } - else { - bot.remDj(userid); - - if (config.enforcement.stepuprules.waittype == 'SONGS') { - bot.speak(name + ', please wait ' + pastdjs[i].wait - + ' more songs or ' - + (config.enforcement.ffarules.timeout - Math.floor(waittime/1000)) - + ' more seconds before DJing again.'); - } - else if (config.enforcement.stepuprules.waittype == 'MINUTES') { - var timeremaining = (config.enforcement.stepuprules.length * 60000) - - (new Date().getTime() - pastdjs[i].wait.getTime()); - - bot.speak(name + ', please wait ' + Math.floor(timeremaining / 60000) - + ' minutes and ' + Math.floor((timeremaining % 60000) / 1000) - + ' seconds before DJing again.'); - } - } - } - } +global.checkStepup = function(userid, name) { + //Get time elapsed between previous dj stepping down and this dj stepping up + var waittime = new Date().getTime() - enforcementtimeout.getTime(); + for(i in pastdjs) { + if(pastdjs[i].id == userid) { + //if the user waited longer than the FFA timeout or it's a free-for-all, + //remove from list. Else, remove dj and warn + + if(config.enforcement.ffarules.multiplespotffa && ffa) { + legalstepdown = true; + } + else if(config.enforcement.ffarules.timerffa) { + legalstepdown = (waittime > (config.enforcement.ffarules.timeout * 1000)); + } + else { + legalstepdown = false; + } + + if(legalstepdown) { + for(i in pastdjs) { + if(pastdjs[i].id == userid) { + pastdjs.splice(i, 1); + } + } + } + else { + bot.remDj(userid); + + if(config.enforcement.stepuprules.waittype == 'SONGS') { + bot.speak(name + ', please wait ' + pastdjs[i].wait + + ' more songs or ' + + (config.enforcement.ffarules.timeout - Math.floor(waittime / 1000)) + + ' more seconds before DJing again.'); + } + else if(config.enforcement.stepuprules.waittype == 'MINUTES') { + var timeremaining = (config.enforcement.stepuprules.length * 60000) + - (new Date().getTime() - pastdjs[i].wait.getTime()); + + bot.speak(name + ', please wait ' + Math.floor(timeremaining / 60000) + + ' minutes and ' + Math.floor((timeremaining % 60000) / 1000) + + ' seconds before DJing again.'); + } + } + } + } } -global.checkWaitlist = function (userid, name) { - if (waitlist.length > 0) { - //If they're not first, remove/warn - if (waitlist[0].id == userid) { - waitlist.shift(); - if (djs.length < 5) { - announceNextPersonOnWaitlist(); - } - return true; - } - bot.remDj(userid); - bot.speak(name + ', you\'re not next on the waitlist. Please let ' - + waitlist[0].name + ' up.'); - legalstepdown = false; - return false; - } - return true; +global.checkWaitlist = function(userid, name) { + if(waitlist.length > 0) { + //If they're not first, remove/warn + if(waitlist[0].id == userid) { + waitlist.shift(); + if(djs.length < 5) { + announceNextPersonOnWaitlist(); + } + return true; + } + bot.remDj(userid); + bot.speak(name + ', you\'re not next on the waitlist. Please let ' + + waitlist[0].name + ' up.'); + legalstepdown = false; + return false; + } + return true; } -global.announceNextPersonOnWaitlist = function () { - if (waitlist.length > 0 && djs.length < 5) { - bot.speak('The next spot is for @' + waitlist[0].name + '! You\'ve got 30 seconds to step up!'); - output({text: 'Hey! This spot is yours, so go ahead and step up!', destination: 'pm', - userid: waitlist[0].id}); - - - var waitingfor = waitlist[0].id; - setTimeout(function() { - //See if user has stepped up, if not, call "next" function - if (waitlist.length > 0 && waitlist[0].id == waitingfor) { - waitlist.shift(); - announceNextPersonOnWaitlist(); - } - }, 30000); - } +global.announceNextPersonOnWaitlist = function() { + if(waitlist.length > 0 && djs.length < 5) { + bot.speak('The next spot is for @' + waitlist[0].name + '! You\'ve got 30 seconds to step up!'); + output({text:'Hey! This spot is yours, so go ahead and step up!', destination:'pm', + userid: waitlist[0].id}); + + var waitingfor = waitlist[0].id; + setTimeout(function() { + //See if user has stepped up, if not, call "next" function + if(waitlist.length > 0 && waitlist[0].id == waitingfor) { + waitlist.shift(); + announceNextPersonOnWaitlist(); + } + }, 30000); + } } //Calculates the target number of bonus votes needed for bot to awesome global.getTarget = function() { - if (currentsong.listeners < 11) { - return 3; - } else if (currentsong.listeners < 21) { - return 4; - } - return 5 + Math.floor((currentsong.listeners - 20) / 20); + if(currentsong.listeners < 11) { + return 3; + } else if(currentsong.listeners < 21) { + return 4; + } + return 5 + Math.floor((currentsong.listeners - 20) / 20); } //Calculates the target number of awesomes needed for the bot to awesome global.getVoteTarget = function() { - if (currentsong.listeners <= 3) { - return 2; - } - //Trendline on the average number of awesomes in the 1+Done room - return Math.ceil(Math.pow(1.1383*(currentsong.listeners - 3), 0.6176)); + if(currentsong.listeners <= 3) { + return 2; + } + //Trendline on the average number of awesomes in the 1+Done room + return Math.ceil(Math.pow(1.1383 * (currentsong.listeners - 3), 0.6176)); } //Checks if the user can step up //TODO: Change this to support waitlists (when I implement them) -global.canUserStep = function (name, userid) { - //Case 1: DJ is already on the decks - for (i in djs) { - if (djs[i].id == userid) { - found = true; - return 'You\'re already up!'; - } - } - - //Case 2: fastest-finger - if (config.enforcement.ffarules.multiplespotffa && (djs.length < 4)) { - return 'There\'s more than one spot open, so anyone can step up!'; - } - - //Case 3: Longer than FFA timeout - if (config.enforcement.ffarules.timerffa && (djs.length < 5) - && ((new Date()).getTime() - enforcementtimeout > (config.enforcement.ffarules.length * 1000))) { - return 'It\'s been ' + config.enforcement.ffarules.length + ' seconds, so anyone can step up!'; - } - - //Case 4: DJ in queue - //The bot will tell the user how much longer they must wait - for (i in pastdjs) { - if (pastdjs[i].id == userid) { - if (config.enforcement.stepuprules.waittype == 'SONGS' && config.enforcement.stepuprules.waittostepup) { - if (pastdjs[i].wait == 1) { - return (name + ', please wait one more song.'); - } else { - return (name + ', please wait another ' + pastdjs[i].wait + ' songs.'); - } - } else if (config.enforcement.stepuprules.waittype == 'MINUTES' && config.enforcement.stepuprules.waittostepup) { - var timeremaining = (config.enforcement.stepuprules.length * 60000) - - (new Date().getTime() - pastdjs[i].wait.getTime()); - - return (name + ', please wait ' + Math.floor(timeremaining / 60000) + ' minutes and ' - + Math.floor((timeremaining % 60000)/1000) + ' seconds.'); - } - } - } - - //Case 5: Free to step up, but no spots - if (djs.length == 5) { - return (name + ', you can, but there aren\'t any spots...'); - } - - //Default: Free to step up - return (name + ', go ahead!'); -} +global.canUserStep = function(name, userid) { + //Case 1: DJ is already on the decks + for(i in djs) { + if(djs[i].id == userid) { + found = true; + return 'You\'re already up!'; + } + } + + //Case 2: fastest-finger + if(config.enforcement.ffarules.multiplespotffa && (djs.length < 4)) { + return 'There\'s more than one spot open, so anyone can step up!'; + } + + //Case 3: Longer than FFA timeout + if(config.enforcement.ffarules.timerffa && (djs.length < 5) + && ((new Date()).getTime() - enforcementtimeout > (config.enforcement.ffarules.length * 1000))) { + return 'It\'s been ' + config.enforcement.ffarules.length + ' seconds, so anyone can step up!'; + } + + //Case 4: DJ in queue + //The bot will tell the user how much longer they must wait + for(i in pastdjs) { + if(pastdjs[i].id == userid) { + if(config.enforcement.stepuprules.waittype == 'SONGS' && config.enforcement.stepuprules.waittostepup) { + if(pastdjs[i].wait == 1) { + return (name + ', please wait one more song.'); + } else { + return (name + ', please wait another ' + pastdjs[i].wait + ' songs.'); + } + } else if(config.enforcement.stepuprules.waittype == 'MINUTES' && + config.enforcement.stepuprules.waittostepup) { + var timeremaining = (config.enforcement.stepuprules.length * 60000) + - (new Date().getTime() - pastdjs[i].wait.getTime()); + + return (name + ', please wait ' + Math.floor(timeremaining / 60000) + ' minutes and ' + + Math.floor((timeremaining % 60000) / 1000) + ' seconds.'); + } + } + } + + //Case 5: Free to step up, but no spots + if(djs.length == 5) { + return (name + ', you can, but there aren\'t any spots...'); + } + + //Default: Free to step up + return (name + ', go ahead!'); +}; //Handles chat commands -global.handleCommand = function (name, userid, text, source) { - for (i in commands) { - if (commands[i].matchStart && (text.indexOf(commands[i].name) == 0)) { - commands[i].handler({name: name, userid: userid, text: text, source: source}); - break; - } else if (commands[i].name == text) { - commands[i].handler({name: name, userid: userid, text: text, source: source}); - break; - } - } - - //-------------------------------------- - // Matching commands (regex) - //-------------------------------------- - - //Shuts down bot (only the main admin can run this) - //Disconnects from room, exits process. - if (text.toLowerCase() == (config.botinfo.botname + ', shut down')) { - if (userid == config.admin) { - bot.speak('Shutting down...'); - bot.roomDeregister(); - process.exit(0); - } - } - - //Shuts down bot (only the main admin can run this) - //Disconnects from room, exits process. - if (text.toLowerCase() == (config.botinfo.botname + ', go away')) { - if (userid == config.admin) { - bot.speak('Shutting down...'); - bot.roomDeregister(); - process.exit(33); - } - } - - if (text.toLowerCase() == (config.botinfo.botname + ', come back later')) { - if (userid == config.admin) { - bot.speak('I\'ll be back in ten minutes!'); - bot.roomDeregister(); - process.exit(34); - } - } - - //Have the bot step up to DJ - if (text.toLowerCase() == (config.botinfo.botname + ', step up')) { - if (admincheck(userid)) { - bot.addDj(); - } - } - - //Have the bot jump off the decks - if (text.toLowerCase() == (config.botinfo.botname + ', step down')) { - if (admincheck(userid)) { - bot.remDj(config.botinfo.userid); - } - } - - //Hug bot - if (text.toLowerCase() == ('hugs ' + config.botinfo.botname) || text.toLowerCase() == 'hugs meow') { - var rand = Math.random(); - var timetowait = 1600; - if (rand < 0.4) { - setTimeout(function() { - output({text: 'Awww!', destination: source, userid: userid}); - }, 1500); - timetowait += 600; - } - setTimeout(function() { - var response = ('hugs ' + name); - output({text: response, destination: source, userid: userid}); - }, timetowait); - } - - //Sends a PM to the user - if (text.toLowerCase() == (config.botinfo.botname + ', pm me')) { - if (source == 'speak') { - bot.pm('Hey there! Type "commands" for a list of commands.', userid); - } else if (source == 'pm') { - bot.pm('But... you PM\'d me that. Do you think I\'m stupid? >:T', userid); - } - } +global.handleCommand = function(name, userid, text, source) { + for(i in commands) { + if(commands[i].matchStart && (text.indexOf(commands[i].name) == 0)) { + commands[i].handler({name:name, userid:userid, text:text, source:source}); + break; + } else if(commands[i].name == text) { + commands[i].handler({name:name, userid:userid, text:text, source:source}); + break; + } + } + + //-------------------------------------- + // Matching commands (regex) + //-------------------------------------- + + //Shuts down bot (only the main admin can run this) + //Disconnects from room, exits process. + if(text.toLowerCase() == (config.botinfo.botname + ', shut down')) { + if(userid == config.admin) { + bot.speak('Shutting down...'); + bot.roomDeregister(); + process.exit(0); + } + } + + //Shuts down bot (only the main admin can run this) + //Disconnects from room, exits process. + if(text.toLowerCase() == (config.botinfo.botname + ', go away')) { + if(userid == config.admin) { + bot.speak('Shutting down...'); + bot.roomDeregister(); + process.exit(33); + } + } + + if(text.toLowerCase() == (config.botinfo.botname + ', come back later')) { + if(userid == config.admin) { + bot.speak('I\'ll be back in ten minutes!'); + bot.roomDeregister(); + process.exit(34); + } + } + + //Have the bot step up to DJ + if(text.toLowerCase() == (config.botinfo.botname + ', step up')) { + if(admincheck(userid)) { + bot.addDj(); + } + } + + //Have the bot jump off the decks + if(text.toLowerCase() == (config.botinfo.botname + ', step down')) { + if(admincheck(userid)) { + bot.remDj(config.botinfo.userid); + } + } + + //Hug bot + if(text.toLowerCase() == ('hugs ' + config.botinfo.botname) || text.toLowerCase() == 'hugs meow') { + var rand = Math.random(); + var timetowait = 1600; + if(rand < 0.4) { + setTimeout(function() { + output({text:'Awww!', destination:source, userid:userid}); + }, 1500); + timetowait += 600; + } + setTimeout(function() { + var response = ('hugs ' + name); + output({text:response, destination:source, userid:userid}); + }, timetowait); + } + + //Sends a PM to the user + if(text.toLowerCase() == (config.botinfo.botname + ', pm me')) { + if(source == 'speak') { + bot.pm('Hey there! Type "commands" for a list of commands.', userid); + } else if(source == 'pm') { + bot.pm('But... you PM\'d me that. Do you think I\'m stupid? >:T', userid); + } + } } From aa80ff87f03e1461501b168c087d812393bca705 Mon Sep 17 00:00:00 2001 From: Taylor Hakes Date: Sun, 9 Dec 2012 14:37:50 -0500 Subject: [PATCH 09/29] -added a vote type called OPTIMIZE, votes up if everyone does, votes down if more people vote up then down -changed mysql from createClient to createConnection for support of new mysql function --- events.js | 11 ++++++++++- sparkle.js | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/events.js b/events.js index ed91c7a..6151165 100755 --- a/events.js +++ b/events.js @@ -324,7 +324,16 @@ exports.newSongEventHandler = function (data) { setTimeout(function() { bot.vote('up'); }, randomwait * 1000); - } + } else if(config.bonusvote == 'OPTIMIZE' && currentsong.djid != config.botinfo.userid) { + var time = Math.floor(Math.random() *.6) + .3; + setTimeout(function() { + if(currentsong.down == 0) { + bot.vote('up'); + } else if(currentsong.down > currentsong.up) { + bot.vote('down'); + } + }, time * currentsong.metadata.length * 1000); + } //Decrement partialdjs list for (i in partialdjs) { diff --git a/sparkle.js b/sparkle.js index e9aa51b..5380ec1 100644 --- a/sparkle.js +++ b/sparkle.js @@ -173,7 +173,7 @@ function initializeModules() { dbhost = config.database.login.host; } client = - mysql.createClient({user:config.database.login.user, password:config.database.login.password, database:config.database.dbname, host:dbhost}); + mysql.createConnection({user:config.database.login.user, password:config.database.login.password, database:config.database.dbname, host:dbhost}); } catch(e) { console.log(e); console.log('Make sure that a mysql server instance is running and that the ' From 5baa490fc77fc07fe7106a78f866728c09c68b1d Mon Sep 17 00:00:00 2001 From: Taylor Hakes Date: Sun, 9 Dec 2012 14:48:29 -0500 Subject: [PATCH 10/29] -add 'IF NOT EXISTS' to table creation queries so it would not fail on refresh -added emoticons to after song stats --- events.js | 6 +++--- sparkle.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/events.js b/events.js index 6151165..b8f2546 100755 --- a/events.js +++ b/events.js @@ -283,9 +283,9 @@ exports.endSongEventHandler = function (data) { //Report song stats in chat if (config.responses.reportsongstats) { - var endsongresponse = currentsong.song + ' stats: awesomes: ' - + currentsong.up + ' lames: ' + currentsong.down - + ' snags: ' + currentsong.snags; + var endsongresponse = currentsong.song + ' stats: Awesomes: ' + + currentsong.up + ':+1: Lames: ' + currentsong.down + + ':-1: Snags: ' + currentsong.snags + ':heart:'; if (config.enforcement.waitlist) { endsongresponse += ' waitlist: ' + waitlist.length + ' people.'; } diff --git a/sparkle.js b/sparkle.js index 5380ec1..335ccf2 100644 --- a/sparkle.js +++ b/sparkle.js @@ -237,7 +237,7 @@ function initializeModules() { //Sets up the database global.setUpDatabase = function() { //song table - client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.song + client.query('CREATE TABLE IF NOT EXISTS ' + config.database.dbname + '.' + config.database.tablenames.song + '(id INT(11) AUTO_INCREMENT PRIMARY KEY,' + ' artist VARCHAR(255),' + ' song VARCHAR(255),' @@ -257,7 +257,7 @@ global.setUpDatabase = function() { }); //chat table - client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.chat + client.query('CREATE TABLE IF NOT EXISTS ' + config.database.dbname + '.' + config.database.tablenames.chat + '(id INT(11) AUTO_INCREMENT PRIMARY KEY,' + ' userid VARCHAR(255),' + ' chat VARCHAR(255),' @@ -270,7 +270,7 @@ global.setUpDatabase = function() { }); //user table - client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.user + client.query('CREATE TABLE IF NOT EXISTS ' + config.database.dbname + '.' + config.database.tablenames.user + '(userid VARCHAR(255), ' + 'username VARCHAR(255), ' + 'lastseen DATETIME, ' @@ -282,7 +282,7 @@ global.setUpDatabase = function() { } }); - client.query('CREATE TABLE ' + config.database.dbname + '.' + config.database.tablenames.banned + client.query('CREATE TABLE IF NOT EXISTS ' + config.database.dbname + '.' + config.database.tablenames.banned + '(id INT(11) AUTO_INCREMENT PRIMARY KEY, ' + 'userid VARCHAR(255), ' + 'banned_by VARCHAR(255), ' From d6f63c7d553cec829c2e1ef11f5cbce2841fa4f8 Mon Sep 17 00:00:00 2001 From: Taylor Hakes Date: Tue, 11 Dec 2012 08:26:24 -0500 Subject: [PATCH 11/29] -added laptop to botinfo -changed the optimize voting method to work correctly --- config.sample.json | 3 ++- events.js | 2 +- sparkle.js | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config.sample.json b/config.sample.json index d5b3d60..0d3796b 100644 --- a/config.sample.json +++ b/config.sample.json @@ -1,7 +1,8 @@ {"botinfo": { "auth": "auth+live+#####", "userid": "#####", - "botname":"meow" + "botname":"meow", + "laptop": "linux" }, "roomid": "4f1e02590c4cc807574030f1", "admin": "#####", diff --git a/events.js b/events.js index b8f2546..50443ca 100755 --- a/events.js +++ b/events.js @@ -325,7 +325,7 @@ exports.newSongEventHandler = function (data) { bot.vote('up'); }, randomwait * 1000); } else if(config.bonusvote == 'OPTIMIZE' && currentsong.djid != config.botinfo.userid) { - var time = Math.floor(Math.random() *.6) + .3; + var time = Math.random() *.6 + .3; setTimeout(function() { if(currentsong.down == 0) { bot.vote('up'); diff --git a/sparkle.js b/sparkle.js index 335ccf2..262f04d 100644 --- a/sparkle.js +++ b/sparkle.js @@ -689,8 +689,8 @@ global.canUserStep = function(name, userid) { //Case 3: Longer than FFA timeout if(config.enforcement.ffarules.timerffa && (djs.length < 5) - && ((new Date()).getTime() - enforcementtimeout > (config.enforcement.ffarules.length * 1000))) { - return 'It\'s been ' + config.enforcement.ffarules.length + ' seconds, so anyone can step up!'; + && ((new Date()).getTime() - enforcementtimeout > (config.enforcement.ffarules.timeout * 1000))) { + return 'It\'s been ' + config.enforcement.ffarules.timeout + ' seconds, so anyone can step up!'; } //Case 4: DJ in queue From 86b803f0bd66f56c9bd59aa4f97b13c3f6d6f94b Mon Sep 17 00:00:00 2001 From: Taylor Hakes Date: Fri, 21 Dec 2012 17:58:46 -0500 Subject: [PATCH 12/29] -added lastplayed command for the current song --- commands/lastplayed.js | 22 ++++++++++++++++++++++ events.js | 33 ++++++++++++++++++--------------- sparkle.js | 9 ++------- 3 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 commands/lastplayed.js diff --git a/commands/lastplayed.js b/commands/lastplayed.js new file mode 100644 index 0000000..8f6eab7 --- /dev/null +++ b/commands/lastplayed.js @@ -0,0 +1,22 @@ +exports.name = 'lastplayed'; +exports.hidden = false; +exports.enabled = true; +exports.matchStart = false; +exports.handler = function(data) { + if (config.database.usedb && currentsong._id) { + client.query('SELECT started FROM ' + + config.database.dbname + '.' + config.database.tablenames.song + ' WHERE (songid = \''+ currentsong._id +'\') ORDER BY started DESC LIMIT 1', + function select(error, results, fields) { + var response = ''; + + if(results && results.length > 0) { + response = 'Last played ' + results[0]['started'] + '.'; + } else { + console.log(error); + response = 'This song has not been played before.'; + } + + output({text: response, destination: data.source, userid: data.userid}); + }); + } +} diff --git a/events.js b/events.js index 50443ca..eda0601 100755 --- a/events.js +++ b/events.js @@ -5,7 +5,6 @@ exports.readyEventHandler = function (data) { loop(); - } //Runs when the room is changed. @@ -20,14 +19,15 @@ exports.roomChangedEventHandler = function(data) { } //Creates the dj list - for (i in data.room.metadata.djs) { + for (var i in data.room.metadata.djs) { if (config.enforcement.enforceroom) { - djs.push({id: data.room.metadata.djs[i], remaining: config.enforcement.songstoplay}); + djs.push({id: data.room.metadata.djs[i], remaining: config.enforcement.songstoplay, lastActivity: new Date()}); } else { - djs.push({id: data.room.metadata.djs[i], remaining: 0}); + djs.push({id: data.room.metadata.djs[i], remaining: Infinity}); } } } + checkDjs(); //If the bonus flag is set to VOTE, find the number of awesomes needed for //the current song @@ -192,6 +192,7 @@ exports.deregisteredEventHandler = function (data) { if (config.consolelog) { console.log('\u001b[36m[ Left ] ' + data.user[0].name + '\u001b[0m'); } + currentsong.listeners--; @@ -272,13 +273,6 @@ exports.endSongEventHandler = function (data) { if (config.enforcement.enforceroom) { reducePastDJCounts(currentsong.djid); } - else { - for (i in djs) { - if (djs[i].id == currentsong.djid) { - djs[i].remaining++; - } - } - } //Report song stats in chat @@ -291,8 +285,10 @@ exports.endSongEventHandler = function (data) { } bot.speak(endsongresponse); } - - + + //Delete the current song + currentsong = {}; + checkDjs(); } //Runs when a new song is played @@ -327,7 +323,7 @@ exports.newSongEventHandler = function (data) { } else if(config.bonusvote == 'OPTIMIZE' && currentsong.djid != config.botinfo.userid) { var time = Math.random() *.6 + .3; setTimeout(function() { - if(currentsong.down == 0) { + if(currentsong.down == 0 && currentsong.up > 0) { bot.vote('up'); } else if(currentsong.down > currentsong.up) { bot.vote('down'); @@ -410,6 +406,8 @@ exports.remDjEventHandler = function (data) { // Check if the bot was removed if(isBot(data.user[0].userid)) { isdjing = false; + } else if(currentsong.djid != config.botinfo.userid) { // Don't remove the bot in th middle of a song + checkDjs(); } @@ -452,8 +450,13 @@ exports.addDjEventHandler = function(data) { } else { djs.push({id: data.user[0].userid, remaining: Infinity}); } - + // Check if the bot was added + if(isBot(data.user[0].userid)) { + isdjing = true; + } else if(currentsong.djid != config.botinfo.userid) { // Don't remove the bot in th middle of a song + checkDjs(); + } if (config.enforcement.waitlist) { checkWaitlist(data.user[0].userid, data.user[0].name); diff --git a/sparkle.js b/sparkle.js index 262f04d..5abf9ab 100644 --- a/sparkle.js +++ b/sparkle.js @@ -337,10 +337,7 @@ global.loop = function() { checkAFK(); } - if(config.djing.botdj) { - checkDjs(); - } - }, 5000); + }, 20000); } //TODO: Implement @@ -388,10 +385,8 @@ global.checkDjs = function() { if(isdjing) { return; } - isdjing = true; bot.addDj(); } else if(isdjing) { - isdjing = false; bot.remDj(); } }; @@ -469,7 +464,7 @@ global.welcomeUser = function(name, id) { global.enforceRoom = function() { setTimeout(function() { if(!userstepped) { - bot.speak('@' + usersList[usertostep].name + ', please step down'); + bot.speak('@' + usersList[usertostep].name + ', you have played ' + config.enforcement.songslimit.maxsongs + ' songs. Please step down to allow others to DJ.'); setTimeout(function() { if(!userstepped) { bot.remDj(usertostep); From d78def6fcd699fc0e06dac720fc49f1cf6432cc1 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sat, 22 Dec 2012 23:41:32 -0800 Subject: [PATCH 13/29] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 297ec9e..8022864 100755 --- a/README.md +++ b/README.md @@ -25,7 +25,3 @@ The bot can: * Log song statistics and chat entries in a MySQL database * Receive and respond to commands via a HTTP RESTful API * Manage a waitlist/queue for a room - -## Help - -If you have a **nontrivial** question, feel free to contact me. I'm usually hanging out in the [Indie/Classic Alt 1+Done room](http://turntable.fm/indieclassic_alternative_1_done) on Turntable. \ No newline at end of file From 248cdefc80ff54b54875e2271f77a5a41b971476 Mon Sep 17 00:00:00 2001 From: Scott Emmons Date: Wed, 26 Dec 2012 08:18:41 -0800 Subject: [PATCH 14/29] Fix banning to insert to correct table --- commands/addban.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/addban.js b/commands/addban.js index bb8c2a2..532198d 100644 --- a/commands/addban.js +++ b/commands/addban.js @@ -18,7 +18,7 @@ exports.handler = function(data) { } function addToBanList(userid, name, bannedby) { - client.query('INSERT INTO ' + config.database.dbname + '.' + config.database.tablenames.user + ' SET userid = ?, banned_by = ?, timestamp = NOW()', + client.query('INSERT INTO ' + config.database.dbname + '.' + config.database.tablenames.banned + ' SET userid = ?, banned_by = ?, timestamp = NOW()', [userid, bannedby]); bot.speak(name + ' (UID ' + userid + ') has been banned by ' + bannedby + '.'); bot.boot(userid, 'You have been banned by ' + bannedby + '.'); From 53d8305446db7ddd8deb0bc0a85e8ebf9f66b7ef Mon Sep 17 00:00:00 2001 From: Taylor Hakes Date: Wed, 16 Jan 2013 10:37:24 -0500 Subject: [PATCH 15/29] -fix a bug with turntable and stepping down at the beginning of a song -fix a bug where mysql will randomly disconnect -fix a bug where removing djs would crash the bot --- events.js | 10 ++++++++-- sparkle.js | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/events.js b/events.js index eda0601..66d65a8 100755 --- a/events.js +++ b/events.js @@ -288,7 +288,13 @@ exports.endSongEventHandler = function (data) { //Delete the current song currentsong = {}; - checkDjs(); + + // Check the number of djs at the beginning of song, but wait 10 seconds to fix turntable bug of + // continuing to play after the bot steps down. + setTimeout(function() { + checkDjs(); + }, 10 * 1000); + } //Runs when a new song is played @@ -406,7 +412,7 @@ exports.remDjEventHandler = function (data) { // Check if the bot was removed if(isBot(data.user[0].userid)) { isdjing = false; - } else if(currentsong.djid != config.botinfo.userid) { // Don't remove the bot in th middle of a song + } else if(!isBot(currentsong.djid)) { // Don't remove the bot in th middle of a song checkDjs(); } diff --git a/sparkle.js b/sparkle.js index 5abf9ab..521baed 100644 --- a/sparkle.js +++ b/sparkle.js @@ -174,6 +174,8 @@ function initializeModules() { } client = mysql.createConnection({user:config.database.login.user, password:config.database.login.password, database:config.database.dbname, host:dbhost}); + + } catch(e) { console.log(e); console.log('Make sure that a mysql server instance is running and that the ' @@ -181,6 +183,27 @@ function initializeModules() { console.log('Starting bot without database functionality.'); config.database.usedb = false; } + + handleDisconnect(client); + + function handleDisconnect(client) { + client.on('error', function(err) { + if(!err.fatal) { + return; + } + if(err.code !== 'PROTOCOL_CONNECTION_LOST') { + throw err; + } + if(config.consolelog) { + console.log('Re-connecting lost connection: ' + err.stack); + } + + client = + mysql.createConnection({user:config.database.login.user, password:config.database.login.password, database:config.database.dbname, host:dbhost}); + handleDisconnect(client); + client.connect(); + }); + } } //Initializes request module @@ -353,7 +376,8 @@ global.checkAFK = function() { if(djs.length >= config.enforcement.idle.minDjs) { for(i in djs) { - if(!isBot(djs[i].id) && (new Date()) - djs[i].lastActivity > 1000 * 60 * config.enforcement.idle.idlewarntime) { + if(!isBot(djs[i].id) && + (new Date()) - djs[i].lastActivity > 1000 * 60 * config.enforcement.idle.idlewarntime) { // If they were already warned don't do it again if(djs[i].warned) { return; @@ -367,9 +391,9 @@ global.checkAFK = function() { setTimeout(function() { for(j in djs) { if(id == djs[j].id && djs[j].warned) { - bot.speak('@' + djs[i].user.name + ', you have been idle for ' + + bot.speak('@' + djs[j].user.name + ', you have been idle for ' + config.enforcement.idle.idleremovaltime + ' minutes. You are no longer DJ.'); - bot.remDj(djs[i].id); + bot.remDj(djs[j].id); } } }, 1000 * 60 * (config.enforcement.idle.idleremovaltime - config.enforcement.idle.idlewarntime)); @@ -381,7 +405,7 @@ global.checkAFK = function() { global.checkDjs = function() { var extra = isdjing ? -1 : 0; - if(djs.length + extra >= config.djing.minDjs && djs.length + extra <= config.djing.maxDjs) { + if(djs.length + extra >= config.djing.minDjs && djs.length + extra <= config.djing.maxDjs) { if(isdjing) { return; } @@ -464,7 +488,8 @@ global.welcomeUser = function(name, id) { global.enforceRoom = function() { setTimeout(function() { if(!userstepped) { - bot.speak('@' + usersList[usertostep].name + ', you have played ' + config.enforcement.songslimit.maxsongs + ' songs. Please step down to allow others to DJ.'); + bot.speak('@' + usersList[usertostep].name + ', you have played ' + config.enforcement.songslimit.maxsongs + + ' songs. Please step down to allow others to DJ.'); setTimeout(function() { if(!userstepped) { bot.remDj(usertostep); @@ -489,7 +514,6 @@ global.reducePastDJCounts = function(djid) { } } - //Reduces past DJ counts and removes from past dj list if necessary if(config.enforcement.stepuprules.waittostepup) { @@ -801,3 +825,4 @@ global.handleCommand = function(name, userid, text, source) { } } } + From 77fd285afab1b208ec0828632a01daf968e34118 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sat, 16 Mar 2013 18:32:15 -0700 Subject: [PATCH 16/29] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8022864..c46110a 100755 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ This project is still maintained, but I don't have a lot of time for development Check out the [Get Started guide](https://github.com/sharedferret/Sparkle-Turntable-Bot/wiki/Get-Started) over on the project's [Wiki page](https://github.com/sharedferret/Sparkle-Turntable-Bot/wiki) for detailed installation instructions. +## News +3/16/13 - I'm currently working on updating the bot to use sqlite instead of MySQL. What this means is it will no longer be necessary to set up a database for the bot (it's entirely contained within and managed by Sparkle). This should hopefully make installation much easier. There's no ETA for when this will be done, but if you want to try it out, a (somewhat) working copy is available on the dev branch. + ## Features This bot is written in Node.JS and utilizes Alain Gilbert's Turntable API. From d8a65010fc155379829ac027cf36c914c277a378 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sat, 23 Mar 2013 19:48:42 -0700 Subject: [PATCH 17/29] Require node-mysql 0.9.6 or earlier Updates package.json to require v0.9.6 or earlier of node-mysql, as later versions contain backwards-breaking client changes. Mitigates #94. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b769b6e..1edbe13 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "ttapi": "*", "request": "*", "xml2js": ">= 0.1.13", - "mysql":"*" + "mysql":"<= 0.9.6" }, "devDependencies": {}, "optionalDependencies": {} From 66255f7ba94760937255b13def1e7e059578b5e2 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Mon, 25 Mar 2013 19:17:50 -0700 Subject: [PATCH 18/29] Require ttapi 2.1.8 or later Fixes #100 --- .gitignore | 3 ++- package.json | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 30db447..5490599 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules setup.js .idea .DS_Store -config/ \ No newline at end of file +config/ +db/sparkle.sqlite diff --git a/package.json b/package.json index 1edbe13..9e8398e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.1.2", + "version": "1.1.3", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", @@ -18,7 +18,7 @@ "node": ">= 0.4.0" }, "dependencies" : { - "ttapi": "*", + "ttapi": ">= 2.1.8", "request": "*", "xml2js": ">= 0.1.13", "mysql":"<= 0.9.6" From 74c7a3a38af3dbc3a76afcdea566514506c60f6e Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Mon, 25 Mar 2013 23:12:02 -0700 Subject: [PATCH 19/29] No longer require 0.9.x branch of mysql Forgot that someone already fixed the mysql issue. Changing the package.json dependency on mysql back to latest from 0.9.6 per #103 --- .npmignore | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.npmignore b/.npmignore index 7525fc1..3e66961 100644 --- a/.npmignore +++ b/.npmignore @@ -4,4 +4,4 @@ node_modules/ setup.js .DS_Store config/ - +db/sparkle.sqlite diff --git a/package.json b/package.json index 9e8398e..bf493df 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "ttapi": ">= 2.1.8", "request": "*", "xml2js": ">= 0.1.13", - "mysql":"<= 0.9.6" + "mysql":"*" }, "devDependencies": {}, "optionalDependencies": {} From 13c529e625796c169b2d766e34cf2974dc4cb07a Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Thu, 28 Mar 2013 08:52:13 -0700 Subject: [PATCH 20/29] Don't welcome Guests --- events.js | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/events.js b/events.js index 66d65a8..518bda2 100755 --- a/events.js +++ b/events.js @@ -136,7 +136,8 @@ exports.registeredEventHandler = function (data) { //Greet user //Displays custom greetings for certain members //Wait for user to join chatserver before welcoming - if(config.responses.welcomeusers) { + //Don't welcome guests + if(config.responses.welcomeusers && user.registered != null) { setTimeout(function () { welcomeUser(user.name, user.userid); }, 1000); diff --git a/package.json b/package.json index bf493df..12d5643 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.1.3", + "version": "1.1.5", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", From d395b979742c34ddfe5861fcd0153cc05c76583b Mon Sep 17 00:00:00 2001 From: pratik Date: Wed, 1 May 2013 00:14:10 -0700 Subject: [PATCH 21/29] Add new commands --- commands/betrippy.js | 28 +++++++++++++ commands/boot.js | 18 ++++++++ commands/notrippy.js | 20 +++++++++ commands/recommend.js | 97 +++++++++++++++++++++++++++++++++++++++++++ commands/remmod.js | 19 +++++++++ commands/stalk.js | 26 ++++++++++++ 6 files changed, 208 insertions(+) create mode 100644 commands/betrippy.js create mode 100644 commands/boot.js create mode 100644 commands/notrippy.js create mode 100644 commands/recommend.js create mode 100644 commands/remmod.js create mode 100644 commands/stalk.js diff --git a/commands/betrippy.js b/commands/betrippy.js new file mode 100644 index 0000000..7bb8dc8 --- /dev/null +++ b/commands/betrippy.js @@ -0,0 +1,28 @@ +exports.name = 'betrippy'; +exports.hidden = false; +exports.enabled = true; +exports.matchStart = false; + +global.isTrippy = false; + +exports.handler = function(data) { + if (admincheck(data.userid, data)) { + var botSay = function(txt) { + output({text: txt, destination: data.source, userid: data.userid}); + } + var avatarIds = [9, 10, 11, 12, 13, 14, 15, 16, 17]; + var avChanger = function() { + if (isTrippy) { + var avId = avatarIds[parseInt(Math.random() * avatarIds.length)]; + bot.setAvatar(avId, function(data, err) { + setTimeout(avChanger, 3100); + }); + } + } + if (!isTrippy) { + isTrippy = true; + botSay("Imma be trippy on " + ["DMT", "Bromo-Dragon Fly", "LSD", "Cocaine", "Ecstacy"][ parseInt(Math.random() * 5) ]); + avChanger(); + } + } +} diff --git a/commands/boot.js b/commands/boot.js new file mode 100644 index 0000000..80b7fc6 --- /dev/null +++ b/commands/boot.js @@ -0,0 +1,18 @@ +exports.name = 'bootuser'; +exports.hidden = true; +exports.enabled = true; +exports.matchStart = true; + +exports.handler = function(data) { + if (admincheck(data.userid, data)) { + var nameforid = data.text.substring(10); + bot.getUserId(nameforid, function(d) { + if (d.success) { + var urls = global.booturls; + bot.boot(d.userid, urls[ parseInt(Math.random() * urls.length) ]); + } else { + output({text: "User id not found!", destination: 'pm', userid: data.userid}); + } + }); + } +}; diff --git a/commands/notrippy.js b/commands/notrippy.js new file mode 100644 index 0000000..e87318a --- /dev/null +++ b/commands/notrippy.js @@ -0,0 +1,20 @@ +//Ping bot +//Useful for users that use the iPhone app + +exports.name = 'notrippy'; +exports.hidden = false; +exports.enabled = true; +exports.matchStart = false; + + +exports.handler = function(data) { + if (admincheck(data.userid, data)) { + var botSay = function(txt) { + output({text: txt, destination: data.source, userid: data.userid}); + } + if (isTrippy) { + isTrippy = false; + botSay("Whoa that was one helluva trip. So messed up right now."); + } + } +} diff --git a/commands/recommend.js b/commands/recommend.js new file mode 100644 index 0000000..2d3896c --- /dev/null +++ b/commands/recommend.js @@ -0,0 +1,97 @@ +//Ping bot +//Useful for users that use the iPhone app +var http = require("http"); + +exports.name = 'recommend'; +exports.hidden = false; +exports.enabled = true; +exports.matchStart = true; + +function encodeOptions(data) { + var ret = []; + for (var d in data) + ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d])); + return ret.join("&"); +} + +function makeRequestWithOptions(pathStr, options, callback) { + var url = pathStr + "/?" + encodeOptions(options); + console.log("Calling url " + url); + var options = { + hostname: 'api.beatport.com', + port: 80, + path: url, + method: 'GET' + }; + + var req = http.request(options, function(res) { + var result = ''; + res.on('data', function(d) { + result += d; + }); + + res.on('end', function() { + callback(result); + }); + }); + req.end(); + req.on('error', function(e) { + console.error(e); + }); +} + +exports.handler = function(data) { + var botSay = function(txt) { + output({text: txt, destination: data.source, userid: data.userid}); + } + var processMostPopular = function(result) { + try { + var data = JSON.parse(result); + var songs = data.results; + // Random picker with a modified PDF to prefer songs at the top + var N = songs.length ; + var i = parseInt(Math.pow(Math.random(), 0.95) * N); + var artists = songs[i].artists, artists2 = []; + for (var j=0; j < artists.length; j++) { + artists2.push(artists[j].name); + } + var response = "I'd recommend '" + songs[i].title + "' by '" + artists2.join(", ") + "'. Checkout a sample here " + songs[i].sampleUrl; + botSay(response); + } catch(e) { + console.log("Invalid JSON from beatport :("); + } + }; + + + var parts = data.text.split(" "); + console.log("Parts are " + parts); + var num = parseInt(parts[2]); + if (isNaN(num)) { + num = 10; + } else { + num = Math.min(num, 50); + } + console.log("Number is " + num); + console.log(parts); + var pathStr = '/catalog/3/most-popular'; + if (parts[1] && parts[1] != ".") { + makeRequestWithOptions("/catalog/3/genres", {}, function(result) { + var genreMaps = {} + var genres = []; + JSON.parse(result)['results'].forEach(function(g, i) { + var genreId = g['id'], + genreName = g['slug']; + genreMaps[genreName] = genreId; + genres.push(genreName); + }); + + if (genreMaps[parts[1]]) { + makeRequestWithOptions(pathStr + "/genre", {'perPage': num, 'id': genreMaps[parts[1]]}, processMostPopular); + } else { + botSay("Can't understand genre " + parts[1] + ". Try any from " + genres.join(", ")); + } + }); + } else { + makeRequestWithOptions(pathStr, {'perPage': num}, processMostPopular); + } +} diff --git a/commands/remmod.js b/commands/remmod.js new file mode 100644 index 0000000..ed9af4e --- /dev/null +++ b/commands/remmod.js @@ -0,0 +1,19 @@ +exports.name = 'unmod '; +exports.hidden = false; +exports.enabled = true; +exports.matchStart = true; +exports.handler = function(data) { + if (admincheck(data.userid, data)) { + var nameforid = data.text.substring(7); + bot.getUserId(nameforid, function(d) { + if (d.success) { + var response = nameforid + '\'s (uid = ' + d.userid + ') has been unmodded'; + bot.remModerator(d.userid); + output({text: response, destination: data.source, userid: data.userid}); + } else { + var response = nameforid + ' is not a valid username on TT.fm.'; + output({text: response, destination: data.source, userid: data.userid}); + } + }); + } +} diff --git a/commands/stalk.js b/commands/stalk.js new file mode 100644 index 0000000..ecdd121 --- /dev/null +++ b/commands/stalk.js @@ -0,0 +1,26 @@ +exports.name = '.stalk'; +exports.hidden = true; +exports.enabled = true; +exports.matchStart = true; + +exports.handler = function(data) { + var src = data.userid; + if (admincheck(src, data)) { + + var u = data.text.substring('.stalk'.length + 2); + var say = function(txt) { + output({text: txt, destination: 'pm', userid: src}); + }; + bot.getUserId(u, function(d) { + if (d.success) { + bot.stalk(d.userid, true, function(data) { + var txt = "@" + u + " is in '" + data['room']['name'] + "'"; + say(txt); + console.log("Stalking " + d.userid + " " + txt); + }); + } else { + output({text: "User id not found!", destination: 'pm', userid: data.userid}); + } + }); + } +} From 3667bf98a2623caf06e9cda55c487d40975cad45 Mon Sep 17 00:00:00 2001 From: pratik Date: Wed, 1 May 2013 00:19:11 -0700 Subject: [PATCH 22/29] Fallback for boot.js error message --- commands/boot.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/commands/boot.js b/commands/boot.js index 80b7fc6..e4bbd82 100644 --- a/commands/boot.js +++ b/commands/boot.js @@ -9,7 +9,8 @@ exports.handler = function(data) { bot.getUserId(nameforid, function(d) { if (d.success) { var urls = global.booturls; - bot.boot(d.userid, urls[ parseInt(Math.random() * urls.length) ]); + var msg = urls && urls.length > 0? urls[ parseInt(Math.random() * urls.length) ] : "You've been booted by the bot"; + bot.boot(d.userid, msg); } else { output({text: "User id not found!", destination: 'pm', userid: data.userid}); } From 4b506bb2e1ef66388d6b0914ee2c66ed20dee5e1 Mon Sep 17 00:00:00 2001 From: pratik Date: Wed, 1 May 2013 22:09:20 -0700 Subject: [PATCH 23/29] Add error catching, so process doesn't die on errors in non-critical parts like poorly written commands. Support exports.name being an array and not just a string. Add a webserver on port 8080 so that bot can be monitored via something like pingdom. Fix MySQL reconnection issue Optionally send users a message saying that they're not mods when they use a mod command --- config.sample.json | 5 +- sparkle.js | 171 ++++++++++++++++++++++++++++++++------------- 2 files changed, 128 insertions(+), 48 deletions(-) diff --git a/config.sample.json b/config.sample.json index 0d3796b..365ac74 100644 --- a/config.sample.json +++ b/config.sample.json @@ -82,4 +82,7 @@ }, "maintenance":{ "autorejoin":true - }} + }, + modnotice: false, + webstatus: false +} diff --git a/sparkle.js b/sparkle.js index 521baed..a7a0a02 100644 --- a/sparkle.js +++ b/sparkle.js @@ -14,6 +14,39 @@ * */ var args = process.argv; +var http = require('http'); + +if (config.webstatus) { + var ipaddr = process.env.OPENSHIFT_NODEJS_IP || "127.0.0.1"; + var port = process.env.OPENSHIFT_NODEJS_PORT || 8080; + + http.createServer(function (req, res) { + var addr = "unknown"; + var out = ""; + if (req.headers.hasOwnProperty('x-forwarded-for')) { + addr = req.headers['x-forwarded-for']; + } else if (req.headers.hasOwnProperty('remote-addr')){ + addr = req.headers['remote-addr']; + } + + if (req.headers.hasOwnProperty('accept')) { + if (req.headers['accept'].toLowerCase() == "application/json") { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify({'ip': addr}, null, 4) + "\n"); + return ; + } + } + + res.writeHead(200, {'Content-Type': 'text/html'}); + res.write("Welcome to this bot. Can't tell you much here though!"); + res.write('Visit us at TT.
'); + res.write("Your IP address seems to be " + addr + "
"); + client.query('SELECT count(*) as total FROM '+ config.database.dbname + '.' + config.database.tablenames.song, + function select(error, results, fields) { + res.end(results[0]['total'] + " songs have been played here"); + }); + }).listen(port, ipaddr); +} global.package = require('./package.json'); global.fs = require('fs'); @@ -56,6 +89,13 @@ global.bonuspoints = new Array(); //An array of DJs wanting the bot to bonu global.bonusvote = false; //A flag denoting if the bot has bonus'd a song global.bonusvotepoints = 0; //The number of awesomes needed for the bot to awesome +global.nomodurls = ['http://i.imgur.com/8Nb4gu2.jpg', + 'http://i.imgur.com/LP977oP.gif', + 'I know - http://i.imgur.com/kgdbKsw.gif', + 'http://i.imgur.com/sjRv3Sv.gif', + 'http://i.imgur.com/xLhBEMC.gif', + ]; + //Current song info global.currentsong = { artist: null, @@ -165,28 +205,19 @@ function initializeModules() { console.log('Starting bot without database functionality.'); config.database.usedb = false; } - - //Connects to mysql server - try { - var dbhost = 'localhost'; + + function createMySqlConnection() { + var dbhost = 'localhost'; if(config.database.login.host != null && config.database.login.host != '') { dbhost = config.database.login.host; } - client = - mysql.createConnection({user:config.database.login.user, password:config.database.login.password, database:config.database.dbname, host:dbhost}); - - - } catch(e) { - console.log(e); - console.log('Make sure that a mysql server instance is running and that the ' - + 'username and password information in config.js are correct.'); - console.log('Starting bot without database functionality.'); - config.database.usedb = false; - } - - handleDisconnect(client); - - function handleDisconnect(client) { + return mysql.createConnection({user:config.database.login.user, + password:config.database.login.password, + database:config.database.dbname, + host: config.database.login.host});// */ + } + + function handleDisconnect(client) { client.on('error', function(err) { if(!err.fatal) { return; @@ -194,16 +225,24 @@ function initializeModules() { if(err.code !== 'PROTOCOL_CONNECTION_LOST') { throw err; } - if(config.consolelog) { - console.log('Re-connecting lost connection: ' + err.stack); - } - - client = - mysql.createConnection({user:config.database.login.user, password:config.database.login.password, database:config.database.dbname, host:dbhost}); - handleDisconnect(client); + console.log('Re-connecting lost connection: ' + err.stack); + client = createMySqlConnection(); + handleDisconnect(client); client.connect(); }); } + + //Connects to mysql server + try { + client = createMySqlConnection(); + } catch(e) { + console.log(e); + console.log('Make sure that a mysql server instance is running and that the ' + + 'username and password information in config.js are correct.'); + console.log('Starting bot without database functionality.'); + config.database.usedb = false; + } + handleDisconnect(client); } //Initializes request module @@ -227,8 +266,9 @@ function initializeModules() { } //Create HTTP listeners + if(config.http.usehttp) { - bot.listen(config.http.port, config.http.host); + bot.listen(config.http.port, config.http.host || process.env.OPENSHIFT_NODEJS_IP); } //Load commands @@ -236,7 +276,7 @@ function initializeModules() { var filenames = fs.readdirSync('./commands'); for(i in filenames) { var command = require('./commands/' + filenames[i]); - commands.push({name:command.name, handler:command.handler, hidden:command.hidden, + commands.push({name:command.name, handler:command.handler, hidden:command.hidden, enabled: command.enabled, matchStart:command.matchStart}); } } catch(e) { @@ -347,11 +387,19 @@ global.output = function(data) { //Checks if the user id is present in the admin list. Authentication //for admin-only privileges. -global.admincheck = function(userid) { - return (userid === config.admin || +global.admincheck = function(userid, data) { + var isAdmin = (userid === config.admin || moderators.some(function(moderatorid) { return moderatorid === userid; })); + if (data && !isAdmin && config.modnotice) { + var urls = global.nomodurls; + global.output({text: 'Thats a mod command - ' + + urls[parseInt(Math.random()*urls.length)], + destination: 'pm', + userid: data.userid}); + } + return isAdmin; } global.loop = function() { @@ -461,13 +509,9 @@ global.addToDb = function(data) { global.welcomeUser = function(name, id) { //Ignore ttstats bots if(!name.match(/^ttstats/)) { - if(id == '4f5628b9a3f7515810008122') { - bot.speak(':cat: <3 :wolf:'); - } - else if(id == '4df0443f4fe7d0631905d6a8') { + if(id == '4df0443f4fe7d0631905d6a8') { bot.speak(':cat: <3 ' + name); - } - else if(config.database.usedb) { + } else if(config.database.usedb) { client.query('SELECT greeting FROM ' + config.database.dbname + '.' + config.database.tablenames.holiday + ' WHERE date LIKE CURDATE()', function cbfunc(error, results, fields) { @@ -478,7 +522,7 @@ global.welcomeUser = function(name, id) { } }); } else { - bot.speak(config.responses.greeting + name + '!'); + bot.speak(config.responses.greeting + name + '! Watch your (dub)step.'); } } } @@ -505,7 +549,10 @@ global.reducePastDJCounts = function(djid) { if(config.enforcement.songslimit.limitsongs && djs.length >= config.enforcement.songslimit.minDjs) { for(i in djs) { if(djs[i].id == djid) { - djs[i].remaining--; + if (!djs[i].remaining) { + djs[i].remaining = config.enforcement.songslimit.maxsongs || 1; + } + djs[i].remaining--; if(djs[i].remaining <= 0) { userstepped = false; usertostep = djid; @@ -645,10 +692,19 @@ global.checkWaitlist = function(userid, name) { } return true; } + bot.remDj(userid); bot.speak(name + ', you\'re not next on the waitlist. Please let ' + waitlist[0].name + ' up.'); - legalstepdown = false; + + // If a DJ is escorted add to queue in 300 ms + if (userid != config.botinfo.userid) { + setTimeout(function() { + global.addToWaitlist(userid, name, 'speak'); + }, 300); + } + + legalstepdown = false; return false; } return true; @@ -673,7 +729,7 @@ global.announceNextPersonOnWaitlist = function() { //Calculates the target number of bonus votes needed for bot to awesome global.getTarget = function() { - if(currentsong.listeners < 11) { + if(currentsong.listeners < 11) { return 3; } else if(currentsong.listeners < 21) { return 4; @@ -745,13 +801,31 @@ global.canUserStep = function(name, userid) { //Handles chat commands global.handleCommand = function(name, userid, text, source) { for(i in commands) { - if(commands[i].matchStart && (text.indexOf(commands[i].name) == 0)) { - commands[i].handler({name:name, userid:userid, text:text, source:source}); - break; - } else if(commands[i].name == text) { - commands[i].handler({name:name, userid:userid, text:text, source:source}); - break; - } + var comms = null; + if (commands[i].name instanceof Array) { + comms = commands[i].name; + } else { + comms = [commands[i].name]; + } + var found = false; + for (var x in comms) { + if (comms[x] instanceof RegExp) { + if (comms[x].test(text)) { + found = true; + break; + } + } else if(commands[i].matchStart && (text.indexOf(comms[x]) == 0)) { + found = true; + break; + } else if(comms[x] == text) { + found = true; + break; + } + } + if (found) { + commands[i].handler({name:name, userid:userid, text:text, source:source}); + break; + } } //-------------------------------------- @@ -826,3 +900,6 @@ global.handleCommand = function(name, userid, text, source) { } } +process.on('uncaughtException', function (err) { + console.log(err.stack); +}); From 541a291d956fc2165e0c23f86914eccb74e8dfbb Mon Sep 17 00:00:00 2001 From: Adam Fontenot Date: Wed, 12 Jun 2013 15:36:44 -0500 Subject: [PATCH 24/29] Update config.sample.json --- config.sample.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.sample.json b/config.sample.json index 365ac74..353a0f5 100644 --- a/config.sample.json +++ b/config.sample.json @@ -83,6 +83,6 @@ "maintenance":{ "autorejoin":true }, - modnotice: false, - webstatus: false + "modnotice": false, + "webstatus": false } From 2803715ee7f2c0aca458e0c87c3ca5826d6243fa Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Thu, 13 Jun 2013 23:21:52 -0700 Subject: [PATCH 25/29] Fix getpicture Changes getpicture to use the new format that TT vends for a user's Facebook profile. Also removes the Twitter lookup since the 1.0 API is deprecated. --- commands/getpicture.js | 20 ++++---------------- commands/version.js | 2 +- package.json | 2 +- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/commands/getpicture.js b/commands/getpicture.js index c5aba05..651ef66 100644 --- a/commands/getpicture.js +++ b/commands/getpicture.js @@ -7,25 +7,13 @@ exports.handler = function(data) { bot.getUserId(nameforid, function(iddata) { if (iddata.success) { bot.getProfile(iddata.userid, function(d) { - var response; + var response = ''; + console.log('profile', d); //Try Facebook + //TODO: Re-add Twitter profile picture check (requires 1.1 OAuth) if (d.facebook != null && d.facebook != '') { - var loc = d.facebook.indexOf('facebook.com/'); - if (loc >= 0) { - var innerloc = d.facebook.indexOf('profile.php?id='); - var profileid = d.facebook.substring(loc + 13); - if (innerloc >= 0) { - profileid = d.facebook.substring(innerloc + 15); - } - response = d.name + '\'s picture: https://graph.facebook.com/' + profileid + '/picture?type=large'; - } - } - - //Try Twitter - else if (d.twitter != null && d.twitter != '') { - response = d.name + '\'s picture: https://api.twitter.com/1' - + '/users/profile_image?screen_name=' + d.twitter + '&size=bigger'; + response = d.name + '\'s picture: https://graph.facebook.com/' + d.facebook + '/picture?type=large'; } else { response = 'I can\'t find one.'; diff --git a/commands/version.js b/commands/version.js index 56a4ec0..14c5371 100644 --- a/commands/version.js +++ b/commands/version.js @@ -7,7 +7,7 @@ exports.handler = function(data) { function (error, response, body) { var currentversion = JSON.parse(body).version; var response; - if (package.version != currentversion) { + if (package.version < currentversion) { setTimeout(function() { output({text: 'Your version of sparklebot is out of date! Update through npm or at http://git.io/meow', destination: data.source, userid: data.userid}); }, 1000); diff --git a/package.json b/package.json index 12d5643..a1f3765 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.1.5", + "version": "1.1.6", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", From c69ffc7f98df34804fd435a70b7c7b5494db9722 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Thu, 13 Jun 2013 23:37:25 -0700 Subject: [PATCH 26/29] Small changes --- README.md | 3 --- package.json | 2 +- sparkle.js | 23 ++++------------------- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index c46110a..8022864 100755 --- a/README.md +++ b/README.md @@ -10,9 +10,6 @@ This project is still maintained, but I don't have a lot of time for development Check out the [Get Started guide](https://github.com/sharedferret/Sparkle-Turntable-Bot/wiki/Get-Started) over on the project's [Wiki page](https://github.com/sharedferret/Sparkle-Turntable-Bot/wiki) for detailed installation instructions. -## News -3/16/13 - I'm currently working on updating the bot to use sqlite instead of MySQL. What this means is it will no longer be necessary to set up a database for the bot (it's entirely contained within and managed by Sparkle). This should hopefully make installation much easier. There's no ETA for when this will be done, but if you want to try it out, a (somewhat) working copy is available on the dev branch. - ## Features This bot is written in Node.JS and utilizes Alain Gilbert's Turntable API. diff --git a/package.json b/package.json index a1f3765..21fdce4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.1.6", + "version": "1.1.7", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", diff --git a/sparkle.js b/sparkle.js index a7a0a02..3da1c05 100644 --- a/sparkle.js +++ b/sparkle.js @@ -88,13 +88,6 @@ global.moderators = new Array(); global.bonuspoints = new Array(); //An array of DJs wanting the bot to bonus global.bonusvote = false; //A flag denoting if the bot has bonus'd a song global.bonusvotepoints = 0; //The number of awesomes needed for the bot to awesome - -global.nomodurls = ['http://i.imgur.com/8Nb4gu2.jpg', - 'http://i.imgur.com/LP977oP.gif', - 'I know - http://i.imgur.com/kgdbKsw.gif', - 'http://i.imgur.com/sjRv3Sv.gif', - 'http://i.imgur.com/xLhBEMC.gif', - ]; //Current song info global.currentsong = { @@ -292,7 +285,7 @@ function initializeModules() { enabled: command.enabled}); } } catch(e) { - // + console.log('Failed to load HTTP command: ', e); } } @@ -394,8 +387,7 @@ global.admincheck = function(userid, data) { })); if (data && !isAdmin && config.modnotice) { var urls = global.nomodurls; - global.output({text: 'Thats a mod command - ' + - urls[parseInt(Math.random()*urls.length)], + global.output({text: 'Sorry, that command can only be used by a moderator.', destination: 'pm', userid: data.userid}); } @@ -522,7 +514,7 @@ global.welcomeUser = function(name, id) { } }); } else { - bot.speak(config.responses.greeting + name + '! Watch your (dub)step.'); + bot.speak(config.responses.greeting + name + '!'); } } } @@ -532,6 +524,7 @@ global.welcomeUser = function(name, id) { global.enforceRoom = function() { setTimeout(function() { if(!userstepped) { + //TODO: song/songs bot.speak('@' + usersList[usertostep].name + ', you have played ' + config.enforcement.songslimit.maxsongs + ' songs. Please step down to allow others to DJ.'); setTimeout(function() { @@ -852,14 +845,6 @@ global.handleCommand = function(name, userid, text, source) { } } - if(text.toLowerCase() == (config.botinfo.botname + ', come back later')) { - if(userid == config.admin) { - bot.speak('I\'ll be back in ten minutes!'); - bot.roomDeregister(); - process.exit(34); - } - } - //Have the bot step up to DJ if(text.toLowerCase() == (config.botinfo.botname + ', step up')) { if(admincheck(userid)) { From f7321b554ce5e218a029b2316e92563a2afd3f8a Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Mon, 17 Jun 2013 20:37:55 -0700 Subject: [PATCH 27/29] Cleanup from pull requests Making the bot run again --- package.json | 2 +- sparkle.js | 65 ++++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 21fdce4..79a885c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.1.7", + "version": "1.1.8", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", diff --git a/sparkle.js b/sparkle.js index 3da1c05..49ee5f8 100644 --- a/sparkle.js +++ b/sparkle.js @@ -16,37 +16,6 @@ var args = process.argv; var http = require('http'); -if (config.webstatus) { - var ipaddr = process.env.OPENSHIFT_NODEJS_IP || "127.0.0.1"; - var port = process.env.OPENSHIFT_NODEJS_PORT || 8080; - - http.createServer(function (req, res) { - var addr = "unknown"; - var out = ""; - if (req.headers.hasOwnProperty('x-forwarded-for')) { - addr = req.headers['x-forwarded-for']; - } else if (req.headers.hasOwnProperty('remote-addr')){ - addr = req.headers['remote-addr']; - } - - if (req.headers.hasOwnProperty('accept')) { - if (req.headers['accept'].toLowerCase() == "application/json") { - res.writeHead(200, {'Content-Type': 'application/json'}); - res.end(JSON.stringify({'ip': addr}, null, 4) + "\n"); - return ; - } - } - - res.writeHead(200, {'Content-Type': 'text/html'}); - res.write("Welcome to this bot. Can't tell you much here though!"); - res.write('Visit us at TT.
'); - res.write("Your IP address seems to be " + addr + "
"); - client.query('SELECT count(*) as total FROM '+ config.database.dbname + '.' + config.database.tablenames.song, - function select(error, results, fields) { - res.end(results[0]['total'] + " songs have been played here"); - }); - }).listen(port, ipaddr); -} global.package = require('./package.json'); global.fs = require('fs'); @@ -145,6 +114,38 @@ process.on('message', function(data) { } }); +if (config.webstatus) { + var ipaddr = process.env.OPENSHIFT_NODEJS_IP || "127.0.0.1"; + var port = process.env.OPENSHIFT_NODEJS_PORT || 8080; + + http.createServer(function (req, res) { + var addr = "unknown"; + var out = ""; + if (req.headers.hasOwnProperty('x-forwarded-for')) { + addr = req.headers['x-forwarded-for']; + } else if (req.headers.hasOwnProperty('remote-addr')){ + addr = req.headers['remote-addr']; + } + + if (req.headers.hasOwnProperty('accept')) { + if (req.headers['accept'].toLowerCase() == "application/json") { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify({'ip': addr}, null, 4) + "\n"); + return ; + } + } + + res.writeHead(200, {'Content-Type': 'text/html'}); + res.write("Welcome to this bot. Can't tell you much here though!"); + res.write('Visit us at TT.
'); + res.write("Your IP address seems to be " + addr + "
"); + client.query('SELECT count(*) as total FROM '+ config.database.dbname + '.' + config.database.tablenames.song, + function select(error, results, fields) { + res.end(results[0]['total'] + " songs have been played here"); + }); + }).listen(port, ipaddr); +} + // Functions function initializeModules() { @@ -445,7 +446,7 @@ global.checkAFK = function() { global.checkDjs = function() { var extra = isdjing ? -1 : 0; - if(djs.length + extra >= config.djing.minDjs && djs.length + extra <= config.djing.maxDjs) { + if(djs.length + extra >= config.djing.minDjs && djs.length + extra <= config.djing.maxDjs && config.djing.botdj) { if(isdjing) { return; } From 0e196d490ff783e581627ba8996994d694d6d432 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sat, 30 Nov 2013 16:13:41 -0800 Subject: [PATCH 28/29] Fix mysql setup bug when no db is present, change idle wording * Fixes an issue where the bot would crash if the usedb config option was enabled but no mysql server was running * Updates idle warning/pull messages --- sparkle.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sparkle.js b/sparkle.js index 49ee5f8..1246209 100644 --- a/sparkle.js +++ b/sparkle.js @@ -229,6 +229,7 @@ function initializeModules() { //Connects to mysql server try { client = createMySqlConnection(); + handleDisconnect(client); } catch(e) { console.log(e); console.log('Make sure that a mysql server instance is running and that the ' @@ -236,7 +237,7 @@ function initializeModules() { console.log('Starting bot without database functionality.'); config.database.usedb = false; } - handleDisconnect(client); + } //Initializes request module @@ -426,14 +427,14 @@ global.checkAFK = function() { // Warn the DJ and set timer to remove them djs[i].warned = true; - bot.speak('@' + djs[i].user.name + ', you have been idle for ' + config.enforcement.idle.idlewarntime + - ' minutes. Please Awesome or speak in console to remain dj.'); + bot.speak('Hey @' + djs[i].user.name + ', are you still there? You\'ve been idle for ' + config.enforcement.idle.idlewarntime + + ' minutes. Please awesome or chat to continue spinning.'); (function(id) { setTimeout(function() { for(j in djs) { if(id == djs[j].id && djs[j].warned) { - bot.speak('@' + djs[j].user.name + ', you have been idle for ' + - config.enforcement.idle.idleremovaltime + ' minutes. You are no longer DJ.'); + bot.speak('Sorry @' + djs[j].user.name + ', you\'ve been idle for ' + + config.enforcement.idle.idleremovaltime + ' minutes.'); bot.remDj(djs[j].id); } } From 8499b0845d03bd185200bca40e4b6e56b7ecdb39 Mon Sep 17 00:00:00 2001 From: Kate Wellington Date: Sat, 30 Nov 2013 16:34:52 -0800 Subject: [PATCH 29/29] Bump version --- config.sample.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config.sample.json b/config.sample.json index 365ac74..353a0f5 100644 --- a/config.sample.json +++ b/config.sample.json @@ -83,6 +83,6 @@ "maintenance":{ "autorejoin":true }, - modnotice: false, - webstatus: false + "modnotice": false, + "webstatus": false } diff --git a/package.json b/package.json index 79a885c..3f39650 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sparklebot", "description": "A customizable Turntable.fm room bot.", - "version": "1.1.8", + "version": "1.2.0", "keywords": ["turntable.fm", "turntable", "stickybits"], "repository": { "type": "git", @@ -15,7 +15,7 @@ "SQL Tables": "SQL Tables" }, "engines": { - "node": ">= 0.4.0" + "node": ">= 0.6.0" }, "dependencies" : { "ttapi": ">= 2.1.8",