├── .gitignore ├── README.md ├── app.js ├── package.json ├── public ├── scripts │ ├── db.js │ └── script.js ├── setup.sql └── styles │ └── stylesheet.css ├── routes └── index.js └── views ├── all_filter.ejs ├── home.ejs ├── partials ├── homepage.ejs └── navbar.ejs ├── voter.ejs └── worst_filter.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | secret/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoodBot_BadBot 2 | 3 | Associated website to display current rankings and data: 4 | https://goodbot-badbot.herokuapp.com/ 5 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // set up ====================================================================== 2 | 3 | var express = require('express'), 4 | app = express(), 5 | bodyParser = require('body-parser'), 6 | script = require('./public/scripts/script.js'); 7 | 8 | // ROUTES 9 | var indexRoutes = require('./routes/index'); 10 | 11 | // configuration =============================================================== 12 | 13 | app.use(express.static(__dirname + '/public')); 14 | 15 | app.use(bodyParser.urlencoded({extended: true})); 16 | app.use(bodyParser.json()); 17 | 18 | // routes ====================================================================== 19 | 20 | app.use("/", indexRoutes); 21 | 22 | app.listen(process.env.PORT, process.env.IP, function() { 23 | console.log('GoodBot_BadBot app starting on port ' + process.env.PORT); 24 | }); 25 | 26 | // scrapper ==================================================================== 27 | 28 | // Every 2.15 seconds, call scrape() to search Reddit for new comments 29 | setInterval(function() { 30 | script.scrape(); 31 | }, 2150); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goodbotbadbot", 3 | "version": "1.0.0", 4 | "description": "Reddit bot", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node app.js" 9 | }, 10 | "author": "woodske", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.17.2", 14 | "ejs": "^2.5.6", 15 | "express": "^4.15.3", 16 | "mysql": "^2.13.0", 17 | "snoowrap": "^1.14.0", 18 | "string-similarity": "^1.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /public/scripts/db.js: -------------------------------------------------------------------------------- 1 | // set up ====================================================================== 2 | var mysql = require('mysql'), 3 | snoowrap = require('snoowrap'), 4 | stringSimilarity = require('string-similarity'); 5 | 6 | // configuration =============================================================== 7 | 8 | // DATABASE CONFIGURATION 9 | const con = mysql.createConnection({ 10 | host : process.env.DB_HOST, 11 | user : process.env.DB_USER, 12 | password : process.env.DB_PASSWORD, 13 | database : process.env.DB_DATABASE 14 | }); 15 | 16 | // REDDIT API CONFIGURATION 17 | const r = new snoowrap({ 18 | userAgent : process.env.SNOO_USERAGENT, 19 | clientId : process.env.SNOO_CLIENTID, 20 | clientSecret : process.env.SNOO_CLIENTSECRET, 21 | username : process.env.SNOO_USERNAME, 22 | password : process.env.SNOO_PASSWORD 23 | }); 24 | 25 | r.config({requestDelay: 1000, warnings: false}); 26 | 27 | // export function ============================================================= 28 | 29 | module.exports = { 30 | /** 31 | * @summary Called from app.js to store a hit into the database. Check if the bot 32 | * exists in the database. Generate a score if not, otherwise tally the vote 33 | * @param {string} the bot's name 34 | * @param {string} the voter's name 35 | * @param {string} the vote (good or bad) 36 | * @param {string} the voter's ID 37 | * @param {string} the thread's unique ID 38 | * @returns No return value 39 | * */ 40 | addToDb: function addToDb(bName, vName, vote, voter_id, link_id) { 41 | 42 | var sql = "SELECT botName FROM bot WHERE botName = ?;"; 43 | 44 | con.query(sql, [bName], function(err, result) { 45 | if (err) { 46 | throw (err); 47 | } 48 | /** 49 | * Bot is not in database if result is empty 50 | * */ 51 | if (Object.keys(result).length == 0) { 52 | _botScore(bName, vName, vote, voter_id, link_id); 53 | } else { 54 | console.log(bName + " is already in the database"); 55 | _addVoter(vName); 56 | _voterBotMatch(bName, vName, vote, voter_id, link_id); 57 | } 58 | }); 59 | } 60 | }; 61 | 62 | // helper functions ============================================================ 63 | 64 | /** 65 | * @summary Escapes underscores in usernames to prevent Reddit from italicising text 66 | * @param {string} the username 67 | * @return {string} the escaped username 68 | * */ 69 | function _formatUName (username) { 70 | return (username).replace(/_/g, "\\_"); 71 | } 72 | 73 | /** 74 | * @summary Bot filtering score generator 75 | * @param {string} the bot's name 76 | * @param {string} the voter's name 77 | * @param {string} the vote (good or bad) 78 | * @param {string} the voter's ID 79 | * @param {string} the thread's unique ID 80 | * @returns No return value 81 | * */ 82 | function _botScore (bName, vName, vote, voter_id, link_id) { 83 | 84 | var counter = 30; 85 | var total = 0; 86 | var botScore = 0; 87 | 88 | r.getUser(bName).getComments({limit: counter}).then(function(listing) { 89 | 90 | /** 91 | * If the bot has less than (counter) comments, it is too new and defaults to 0. 92 | * */ 93 | if (listing.length < counter) { 94 | console.log(bName + " has too few comments"); 95 | } else { 96 | var dataPoints = 0; 97 | 98 | listing.forEach(function(value, listIndex) { 99 | for(var i = listIndex; i < listing.length - 1; i++) { 100 | dataPoints++; 101 | total += stringSimilarity.compareTwoStrings(listing[listIndex].body, listing[i+1].body); 102 | } 103 | }); 104 | 105 | botScore = (total/dataPoints).toFixed(2); 106 | console.log(bName + ": " + botScore); 107 | } 108 | /** 109 | * A value of 0.3 or higher is a good indicator this is actually a bot 110 | * */ 111 | if (botScore >= 0.3) { 112 | console.log(bName + " is a bot: " + botScore); 113 | _addBot(bName); 114 | _addVoter(vName); 115 | _voterBotMatch(bName, vName, vote, voter_id, link_id); 116 | } else { 117 | console.log(bName + " is likely not a bot: " + botScore); 118 | } 119 | }); 120 | } 121 | 122 | /** 123 | * @summary Inserts the bot's name into the bot table 124 | * @param {string} the bot's name 125 | * @returns No return value 126 | * */ 127 | function _addBot (bName) { 128 | 129 | var sql = "INSERT INTO bot (botName, goodCount, badCount) VALUES (?, 0, 0)"; 130 | 131 | con.query(sql, [bName], function(err, result) { 132 | if (err) { 133 | if (err.code == "ER_DUP_ENTRY") { 134 | console.log(bName + " is already in the database"); 135 | } else { 136 | throw(err); 137 | } 138 | } else { 139 | console.log(bName + " was inserted into the bot table"); 140 | } 141 | }); 142 | } 143 | 144 | /** 145 | * @summary Inserts the voter's name into the voter table if it does not exist 146 | * @param {string} the voter's name 147 | * @returns No return value 148 | * */ 149 | function _addVoter (vName) { 150 | 151 | var sql = "SELECT voterName FROM voter WHERE voterName = ?;"; 152 | 153 | con.query(sql, [vName], function(err, result) { 154 | if (err) { 155 | throw (err); 156 | } 157 | /** 158 | * Bot is not in database if result is empty 159 | * */ 160 | if (Object.keys(result).length == 0) { 161 | var sql = "INSERT INTO voter (voterName) VALUES (?)"; 162 | con.query(sql, [vName], function(err, result) { 163 | if (err) { 164 | if (err.code == "ER_DUP_ENTRY") 165 | console.log(vName + " is already in the database"); 166 | else 167 | throw (err); 168 | } else { 169 | console.log(vName + " was inserted into the voter table"); 170 | } 171 | }); 172 | } else { 173 | console.log(vName + " is already in the database"); 174 | } 175 | }); 176 | } 177 | 178 | /** 179 | * @summary Determines if the voter has voted on the bot before 180 | * @param {string} the bot's name 181 | * @param {string} the voter's name 182 | * @param {string} the vote (good or bad) 183 | * @param {string} the voter's ID 184 | * @returns no return value 185 | * */ 186 | function _voterBotMatch (bName, vName, vote, voter_id, link_id) { 187 | 188 | var sql = "SELECT * FROM bot INNER JOIN bot_voter ON bot.bot_id = bot_voter.bot_id INNER JOIN voter ON bot_voter.voter_id = voter.voter_id " + 189 | "WHERE bot.botName = ? AND voter.voterName = ?;"; 190 | 191 | con.query(sql, [bName, vName], function(err, result) { 192 | if (err) { 193 | throw (err); 194 | } 195 | /** 196 | * an empty object will return if there is no match between the voter and bot 197 | * */ 198 | if (Object.keys(result).length == 0) { 199 | console.log(vName + " has not yet voted for " + bName); 200 | _createMatch(bName, vName, vote); 201 | _addVoteToBot(bName, vote); 202 | _replyToComment(vName, bName, voter_id, link_id); 203 | } else { 204 | console.log(vName + " has already voted for " + bName); 205 | } 206 | }); 207 | } 208 | 209 | /** 210 | * @summary Creates a match in the bot_voter table 211 | * @param {string} the bot's name 212 | * @param {string} the voter's name 213 | * @returns No return value 214 | * */ 215 | function _createMatch (bName, vName, vote) { 216 | /** 217 | * Insert the bot ID, voter ID, vote, and time/date into the bot_voter table to prevent duplicate votes 218 | * */ 219 | var date = new Date(); 220 | var hour, day, month, year; 221 | 222 | hour = date.getHours(); 223 | day = date.getDate(); 224 | month = date.getMonth(); 225 | year = date.getFullYear(); 226 | 227 | var sql = "INSERT INTO bot_voter (bot_id, voter_id, vote, time, vote_hour, vote_day, vote_month, vote_year) VALUES ((SELECT bot_id FROM bot WHERE botName = ?), " + 228 | "(SELECT voter_id FROM voter WHERE voterName = ?), ?, ?, ?, ?, ?, ?);"; 229 | 230 | con.query(sql, [bName, vName, vote, JSON.stringify(date), hour, day, month, year], function(err, result) { 231 | if (err) 232 | throw (err); 233 | else 234 | console.log("Stored that " + vName + " has voted for " + bName + " where:\nHour: " + hour + "\nDay: " + day + 235 | "\nMonth: " + month + "\nYear: " + year); 236 | }); 237 | } 238 | 239 | /** 240 | * @summary Increments the bot's goodCount or badCount by 1 in the bot table 241 | * @param {string} the bot's name 242 | * @param {string} the vote (good or bad) 243 | * @returns No return value 244 | * */ 245 | function _addVoteToBot(bName, vote) { 246 | /** 247 | * Increment the goodCount or badCount depending on the voter comment 248 | * */ 249 | if (vote == "good") { 250 | 251 | var sql = "UPDATE bot SET goodCount = goodCount + 1 WHERE botName = ?;"; 252 | 253 | con.query(sql, [bName], function(err, result) { 254 | if (err) 255 | throw (err); 256 | else 257 | console.log("Added a good bot vote to " + bName); 258 | }); 259 | } else { 260 | 261 | var sql = "UPDATE bot SET badCount = badCount + 1 WHERE botName = ?;"; 262 | 263 | con.query(sql, [bName], function(err, result) { 264 | if (err) 265 | throw (err); 266 | else 267 | console.log("Added a bad bot vote to " + bName); 268 | }); 269 | } 270 | } 271 | 272 | /** 273 | * @summary Reply to voter (once per thread) 274 | * @param {string} the voter's name 275 | * @param {string} the bot's name 276 | * @param {string} the voter's ID 277 | * @param {string} the thread's unique ID 278 | * @returns No return value 279 | * */ 280 | function _replyToComment(vName, bName, voter_id, link_id) { 281 | 282 | var message = "Thank you, " + _formatUName(vName) + ", for voting on " + _formatUName(bName) + ". \n\n" + 283 | "This bot wants to find the best and worst bots on Reddit. [You can view results here](" + process.env.RESULTS_LINK + ")." + 284 | " \n\n *** \n\n" + 285 | "^^Even ^^if ^^I ^^don't ^^reply ^^to ^^your ^^comment, ^^I'm ^^still ^^listening ^^for ^^votes. " + 286 | "^^Check ^^the ^^webpage ^^to ^^see ^^if ^^your ^^vote ^^registered!"; 287 | 288 | var sql = "SELECT link_id FROM link WHERE link_id = ?;"; 289 | 290 | con.query(sql, [link_id], function(err, result) { 291 | if (err) { 292 | throw (err); 293 | } 294 | /** 295 | * link_id is not in database if result is empty 296 | * */ 297 | if (Object.keys(result).length == 0) { 298 | /** 299 | * Reply to voter with a link to the results page if there is no reply in the thread. 300 | * */ 301 | console.log("Replying to " + vName); 302 | r.getSubmission(voter_id).reply(message); 303 | 304 | /** 305 | * Insert the link_id into the link table 306 | * */ 307 | var sql = "INSERT INTO link (link_id) VALUES (?)"; 308 | 309 | con.query(sql, [link_id], function(err, result) { 310 | if (err) { 311 | if (err.code == "ER_DUP_ENTRY") { 312 | console.log(link_id + " is already in the database"); 313 | } else { 314 | throw(err); 315 | } 316 | } else { 317 | console.log(link_id + " was inserted into the link table"); 318 | } 319 | }); 320 | } else { 321 | console.log("Thread " + link_id + " already has a bot comment"); 322 | } 323 | }); 324 | } 325 | -------------------------------------------------------------------------------- /public/scripts/script.js: -------------------------------------------------------------------------------- 1 | // set up ====================================================================== 2 | var snoowrap = require('snoowrap'), 3 | db = require('./db.js'); 4 | 5 | // configuration =============================================================== 6 | 7 | // REDDIT API CONFIGURATION 8 | const r = new snoowrap({ 9 | userAgent : process.env.SNOO_USERAGENT, 10 | clientId : process.env.SNOO_CLIENTID, 11 | clientSecret : process.env.SNOO_CLIENTSECRET, 12 | username : process.env.SNOO_USERNAME, 13 | password : process.env.SNOO_PASSWORD 14 | }); 15 | 16 | r.config({requestDelay: 1000, warnings: false}); 17 | 18 | // export function ============================================================= 19 | 20 | module.exports = { 21 | 22 | /** 23 | * Grab the 100 newest comments from /r/all and check if 24 | * the comment says "good bot" or "bad bot". If so, 25 | * obtain the parent name, commenter's name, and good/bad result 26 | * to store in the database. 27 | * */ 28 | scrape: function() { 29 | /** 30 | * commentObj stores a returned promise containing 100 comments as JSON 31 | * */ 32 | var commentObj = r.getNewComments('all', { 33 | limit: 100 34 | }); 35 | 36 | commentObj.then(function(listing) { 37 | listing.forEach(function(key) { 38 | /** 39 | * Check if comment meets the search criteria. 40 | * If so, pass the comment object and vote to storeVote() to 41 | * handle the database insertions and commenting 42 | * */ 43 | var comment = key.body.substring(0,8).toLowerCase(); 44 | 45 | 46 | if(comment.includes("good bot")) { 47 | console.log("Found comment '" + key.body + "'"); 48 | _storeVote(key, "good"); 49 | } 50 | else if(comment.includes("bad bot")) { 51 | console.log("Found comment '" + key.body + "'"); 52 | _storeVote(key, "bad"); 53 | } 54 | }); 55 | }); 56 | } 57 | }; 58 | 59 | // helper function ============================================================= 60 | 61 | /** 62 | * @summary Grabs the parent comment's name and sends relevant information 63 | * to addToDb(); 64 | * @param {object} comment object containing the comment's metadata 65 | * @param {string} the vote (good or bad) 66 | * @returns No return value 67 | * */ 68 | function _storeVote(commentObj, result) { 69 | /** 70 | * The type prefix ("t1_") indicates that the comment's parent is a comment 71 | * */ 72 | if (commentObj.parent_id.substring(0,2) == "t1") { 73 | var voterName = commentObj.author.name; 74 | console.log("The voter is " + voterName); 75 | 76 | /** 77 | * Find the username of the parent comment. This is the bot's name. 78 | * */ 79 | r.getComment(commentObj.parent_id).fetch().then(function (obj) { 80 | var botName = obj.author.name; 81 | var voterID = commentObj.name; 82 | var linkID = obj.link_id; 83 | console.log("The bot is " + botName); 84 | /** 85 | * Check if the voter and bot name are the same. If not then 86 | * send bot name, voter name, vote result, and voter ID to addToDb found in 87 | * the db.js file. This handles the database interaction and commenting. 88 | * */ 89 | if (botName != voterName) { 90 | db.addToDb(botName, voterName, result, voterID, linkID); 91 | } 92 | }); 93 | } else { 94 | console.log(voterName + " did not respond to a comment"); 95 | } 96 | } 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /public/setup.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS bot_voter; 2 | DROP TABLE IF EXISTS bot; 3 | DROP TABLE IF EXISTS voter; 4 | 5 | CREATE TABLE bot ( 6 | bot_id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, 7 | botName varchar(255) NOT NULL UNIQUE KEY, 8 | goodCount int(11) NOT NULL, 9 | badCount int(11) NOT NULL 10 | ) ENGINE=InnoDB; 11 | 12 | CREATE TABLE voter ( 13 | voter_id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, 14 | voterName varchar(255) NOT NULL UNIQUE KEY 15 | ) ENGINE=InnoDB; 16 | 17 | CREATE TABLE bot_voter ( 18 | bot_id int(11) NOT NULL, 19 | voter_id int(11) NOT NULL, 20 | PRIMARY KEY (bot_id, voter_id), 21 | vote varchar(255), 22 | time varchar(255), 23 | vote_hour int(11), 24 | vote_day int(11), 25 | vote_month int(11), 26 | vote_year int(11), 27 | FOREIGN KEY (bot_id) REFERENCES bot (bot_id) 28 | ON UPDATE CASCADE 29 | ON DELETE CASCADE, 30 | FOREIGN KEY (voter_id) REFERENCES voter (voter_id) 31 | ON UPDATE CASCADE 32 | ON DELETE CASCADE 33 | ) ENGINE=InnoDB; 34 | 35 | CREATE TABLE link ( 36 | link_id int(11) NOT NULL PRIMARY KEY 37 | ) ENGINE=InnoDB; 38 | 39 | CREATE TABLE skipOver ( 40 | userName varchar(255) NOT NULL PRIMARY KEY 41 | ) ENGINE=InnoDB; -------------------------------------------------------------------------------- /public/styles/stylesheet.css: -------------------------------------------------------------------------------- 1 | h1, h2, h3 { 2 | text-align: center; 3 | } 4 | 5 | .allFilter { 6 | text-align: center; 7 | } 8 | 9 | #hgb { 10 | text-align: right; 11 | color: green; 12 | } 13 | 14 | #hbb { 15 | color: red; 16 | } 17 | 18 | .info_div { 19 | text-align: center; 20 | font-size: 16; 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | // set up ====================================================================== 2 | 3 | var mysql = require("mysql"), 4 | express = require("express"), 5 | router = express.Router(); 6 | 7 | // configuration =============================================================== 8 | 9 | // DATABASE CONFIGURATION 10 | const con = mysql.createConnection({ 11 | host : process.env.DB_HOST, 12 | user : process.env.DB_USER, 13 | password : process.env.DB_PASSWORD, 14 | database : process.env.DB_DATABASE 15 | }); 16 | 17 | // routes ====================================================================== 18 | 19 | // ROUTE FOR HOMEPAGE, DISPLAYS TOP 10 BEST BOTS 20 | router.get('/', function (req, res) { 21 | 22 | var total; //Total number of bot votes 23 | var goodCount; //Total number of good bot votes 24 | var badCount; //Total number of bad bot votes 25 | var bestBotNameArr = []; //Array of best bot names 26 | var bestBotScoreArr = []; //Array of highest confidence interval scores 27 | 28 | /** 29 | * Query for total number of votes 30 | * */ 31 | var sql = "SELECT SUM(goodCount), SUM(badCount), SUM(goodCount) + SUM(badCount) FROM bot;"; 32 | con.query(sql, function(err, result) { 33 | if (err) { 34 | throw (err); 35 | } 36 | total = result[0]['SUM(goodCount) + SUM(badCount)']; 37 | goodCount = result[0]['SUM(goodCount)']; 38 | badCount = result[0]['SUM(badCount)']; 39 | }); 40 | 41 | /** 42 | * Query for top 10 best bots 43 | * */ 44 | var sql = "SELECT botName, ((goodCount + 1.9208) / (goodCount + badCount) - " + 45 | "1.96 * SQRT((goodCount * badCount) / (goodCount + badCount) + 0.9604) / " + 46 | "(goodCount + badCount)) / (1 + 3.8416 / (goodCount + badCount)) " + 47 | "AS ci_lower_bound FROM bot WHERE goodCount + badCount > 0 " + 48 | "ORDER BY ci_lower_bound DESC limit 10;"; 49 | 50 | con.query(sql, function(err, result) { 51 | if (err) 52 | throw (err); 53 | 54 | result.forEach(function(key) { 55 | bestBotNameArr.push(key.botName); 56 | bestBotScoreArr.push(key.ci_lower_bound); 57 | }); 58 | 59 | res.render('home.ejs', 60 | { 61 | total: total, 62 | goodCount: goodCount, 63 | badCount: badCount, 64 | bestBotName: bestBotNameArr, 65 | bestBotScore: bestBotScoreArr 66 | } 67 | ); 68 | 69 | }); 70 | }); 71 | 72 | // ROUTE FOR DISPLAYING TOP 10 WORST BOTS 73 | router.get('/worst_filter', function (req, res) { 74 | 75 | var total; //Total number of bot votes 76 | var goodCount; //Total number of good bot votes 77 | var badCount; //Total number of bad bot votes 78 | var worstBotNameArr = []; //Array of worst bot names 79 | var worstBotScoreArr = []; //Array of lowest confidence interval scores 80 | 81 | /** 82 | * Query for total number of votes 83 | * */ 84 | var sql = "SELECT SUM(goodCount), SUM(badCount), SUM(goodCount) + SUM(badCount) FROM bot;"; 85 | con.query(sql, function(err, result) { 86 | if (err) { 87 | throw (err); 88 | } 89 | total = result[0]['SUM(goodCount) + SUM(badCount)']; 90 | goodCount = result[0]['SUM(goodCount)']; 91 | badCount = result[0]['SUM(badCount)']; 92 | }); 93 | 94 | /** 95 | * Query for top 10 worst bots 96 | * */ 97 | var sql = "SELECT botName, ((badCount + 1.9208) / (badCount + goodCount) - " + 98 | "1.96 * SQRT((badCount * goodCount) / (badCount + goodCount) + 0.9604) / " + 99 | "(badCount + goodCount)) / (1 + 3.8416 / (badCount + goodCount)) " + 100 | "AS one_minus_ci_upper_bound FROM bot WHERE badCount + goodCount > 0 " + 101 | "ORDER BY one_minus_ci_upper_bound DESC limit 10;"; 102 | 103 | con.query(sql, function(err, result) { 104 | if (err) 105 | throw (err); 106 | 107 | result.forEach(function(key) { 108 | worstBotNameArr.push(key.botName); 109 | worstBotScoreArr.push(key.one_minus_ci_upper_bound); 110 | }); 111 | 112 | res.render('worst_filter.ejs', 113 | { 114 | total: total, 115 | goodCount: goodCount, 116 | badCount: badCount, 117 | worstBotName: worstBotNameArr, 118 | worstBotScore: worstBotScoreArr 119 | } 120 | ); 121 | 122 | }); 123 | }); 124 | 125 | // ROUTE FOR SHOWING ALL BOTS 126 | router.get('/all_filter', function(req, res) { 127 | 128 | var byBestArr = []; 129 | 130 | /** 131 | * Query for all bots and scores 132 | * */ 133 | var sql = "SELECT botName, goodCount, badCount, ROUND(((goodCount + 1.9208) / (goodCount + badCount) - " + 134 | "1.96 * SQRT((goodCount * badCount) / (goodCount + badCount) + 0.9604) / " + 135 | "(goodCount + badCount)) / (1 + 3.8416 / (goodCount + badCount)),4) " + 136 | "AS ci_lower_bound FROM bot WHERE goodCount + badCount > 0 " + 137 | "ORDER BY ci_lower_bound DESC;"; 138 | 139 | con.query(sql, function(err, result) { 140 | if (err) { 141 | throw (err); 142 | } 143 | byBestArr = result; 144 | res.render('all_filter.ejs', {byBestArr: byBestArr}); 145 | }); 146 | }); 147 | 148 | // ROUTE FOR CHECKING USER VOTES 149 | router.post('/voter', function(req, res) { 150 | 151 | /** 152 | * Query for all bots a voter has voted for 153 | * */ 154 | var sql = "SELECT botName FROM bot INNER JOIN bot_voter ON bot.bot_id = bot_voter.bot_id " + 155 | "INNER JOIN voter ON bot_voter.voter_id = voter.voter_id " + 156 | "WHERE voter.voterName = ? ORDER BY botName;"; 157 | 158 | var inserts = [req.body.voter]; 159 | sql = mysql.format(sql, inserts); 160 | 161 | con.query(sql, function(err, result) { 162 | if (err) { 163 | throw (err); 164 | } 165 | res.render('voter.ejs', {voter: req.body.voter, bot: result}); 166 | }); 167 | }); 168 | 169 | module.exports = router; -------------------------------------------------------------------------------- /views/all_filter.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |Rank | 22 |Bot Name | 23 |Score | 24 |Good Bot Votes | 25 |Bad Bot Votes | 26 |
---|---|---|---|---|
<%= i+1 %> | 32 |'><%= byBestArr[i]['botName'] %> | 33 |<%= byBestArr[i]['ci_lower_bound'] %> | 34 |<%= byBestArr[i]['goodCount'] %> | 35 |<%= byBestArr[i]['badCount'] %> | 36 |
6 | GoodBot_BadBot keeps track of the public's opinion on bots. Each user may vote one time per bot by 7 | replying to the bot with "good bot" or "bad bot". The goal is to find out which bots are the most 8 | popular and least popular. 9 |
10 |11 | Please message GoodBot_BadBot if a user is not a bot, 12 | or if a real bot is not getting through the filtering. 13 |
14 |18 | Use the filter option on the right side of the navigation bar at the top of the page to view the data. 19 | You can also use the search form to check which bots a user has voted on. Click the bars on the graph 20 | to go to each bot's profile (thanks /u/nickburlett for helping me with the links). 21 |
22 |