diff --git a/lib/currency.js b/lib/currency.js index 4a4f3be..bb23f14 100644 --- a/lib/currency.js +++ b/lib/currency.js @@ -134,6 +134,17 @@ function Currency(irc, db, commands, options) { __self.bets_response_timer = 3000; __self.bets_response_reset = true; + // voting settings + __self.votes_status = false; + __self.votes_board = []; + __self.votes_viewers = []; + __self.votes_payout = false; + __self.votes_total = 0; + __self.votes_flood = []; + __self.votes_response = null; + __self.votes_response_timer = 3000; + __self.votes_response_reset = true; + // high score table __self.score_bet_pool = 0; __self.score_bet_single = 0; @@ -283,6 +294,33 @@ Currency.prototype.commands = function (data) { break; } } + + // open / close voting system + if (data[4] === 'vote') { + console.log("vote command found"); + switch (data[5]) { + case 'open': + console.log("vote open command found"); + if (data[6] && data[7]) { + __self.votes(true, data); + } else { + __self.irc.emit('message', {message:__self.pre_text + 'Unable to open voting, need at least two items to vote for'}); + } + break; + case 'close': + __self.votes(false, null); + break; + case 'pool': + __self.votes('pool', null); + break; + case 'winner': + __self.votes('winner', data[6]); + break; + case 'cancel': + __self.votes('cancel'); + break; + } + } // TODO: create a log file that monitors just add/remove/push - greedy mods :D // add currency @@ -432,6 +470,19 @@ Currency.prototype.commands = function (data) { } } } + // cast a vote + if (__self.votes_status === true) { + for (var i = 0; i < __self.votes_board.length; i++) { + if (data[3].slice(1) === '!' + __self.votes_board[i].name) { + if (isNaN(parseInt(data[4], 10)) === false) { + if (data[4] >= 0 && data[4] % 1 === 0) { + __self.collect_votes(__self.irc.caller(data[0]), __self.votes_board[i], parseInt(data[4], 10)); + break; + } + } + } + } + } }; /** @@ -956,48 +1007,50 @@ Currency.prototype.auction = function (status) { switch (status) { case true: - if (!__self.bets_status) { - if (!__self.raffle_status) { - if (!__self.auction_status) { - // open up the auction - __self.auction_status = true; + if (!__self.votes_status) { + if (!__self.bets_status) { + if (!__self.raffle_status) { + if (!__self.auction_status) { + // open up the auction + __self.auction_status = true; - // request toggle - if (__self.temp.raffle_toggle) { - __self.temp.auction_toggle = __self.temp.raffle_toggle; - } else { - __self.temp.auction_toggle = __self.coin_toggle; - } - __self.coin_toggle = false; + // request toggle + if (__self.temp.raffle_toggle) { + __self.temp.auction_toggle = __self.temp.raffle_toggle; + } else { + __self.temp.auction_toggle = __self.coin_toggle; + } + __self.coin_toggle = false; - // default request timer - if (__self.temp.raffle_timer && __self.temp.raffle_timer_reset) { - __self.temp.auction_timer = __self.temp.raffle_timer; - __self.temp.auction_timer_reset = __self.temp.raffle_timer_reset; - } else { - __self.temp.auction_timer = __self.coin_response_timer; - __self.temp.auction_timer_reset = __self.coin_response_reset; - } - __self.coin_response_timer = 3000; - __self.coin_response_reset = true; + // default request timer + if (__self.temp.raffle_timer && __self.temp.raffle_timer_reset) { + __self.temp.auction_timer = __self.temp.raffle_timer; + __self.temp.auction_timer_reset = __self.temp.raffle_timer_reset; + } else { + __self.temp.auction_timer = __self.coin_response_timer; + __self.temp.auction_timer_reset = __self.coin_response_reset; + } + __self.coin_response_timer = 3000; + __self.coin_response_reset = true; - // clear previous bids - __self.auction_bids = []; - __self.auction_previous_bid = {}; + // clear previous bids + __self.auction_bids = []; + __self.auction_previous_bid = {}; - // auction open response - __self.irc.emit('message', {message:__self.pre_text + 'Auction opened, accepting bids'}) + // auction open response + __self.irc.emit('message', {message:__self.pre_text + 'Auction opened, accepting bids'}) + } else { + // auction is already open response + __self.irc.emit('message', {message:__self.pre_text + 'Auction already in progress'}); + } } else { - // auction is already open response - __self.irc.emit('message', {message:__self.pre_text + 'Auction already in progress'}); + // raffle currently running + __self.irc.emit('message', {message:__self.pre_text + 'You must close the raffle before you can open an auction'}); } } else { - // raffle currently running - __self.irc.emit('message', {message:__self.pre_text + 'You must close the raffle before you can open an auction'}); + // gambling currently running + __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open an auction'}); } - } else { - // gambling currently running - __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open an auction'}); } break; case false: @@ -1229,54 +1282,56 @@ Currency.prototype.raffle = function (status) { switch (status) { case true: - if (!__self.bets_status) { - if (!__self.auction_status) { - if (!__self.raffle_status) { - // open up a raffle - __self.raffle_status = true; + if (!__self.votes_status) { + if (!__self.bets_status) { + if (!__self.auction_status) { + if (!__self.raffle_status) { + // open up a raffle + __self.raffle_status = true; - // request toggle - if (__self.temp.auction_toggle) { - __self.temp.raffle_toggle = __self.temp.auction_toggle; - } else { - __self.temp.raffle_toggle = __self.coin_toggle; - } - __self.coin_toggle = false; + // request toggle + if (__self.temp.auction_toggle) { + __self.temp.raffle_toggle = __self.temp.auction_toggle; + } else { + __self.temp.raffle_toggle = __self.coin_toggle; + } + __self.coin_toggle = false; - // default request timer - if (__self.temp.auction_timer && __self.temp.auction_timer_reset) { - __self.temp.raffle_timer = __self.temp.auction_timer; - __self.temp.raffle_timer_reset = __self.temp.auction_timer_reset; - } else { - __self.temp.raffle_timer = __self.coin_response_timer; - __self.temp.raffle_timer_reset = __self.coin_response_reset; - } - __self.coin_response_timer = 3000; - __self.coin_response_reset = true; + // default request timer + if (__self.temp.auction_timer && __self.temp.auction_timer_reset) { + __self.temp.raffle_timer = __self.temp.auction_timer; + __self.temp.raffle_timer_reset = __self.temp.auction_timer_reset; + } else { + __self.temp.raffle_timer = __self.coin_response_timer; + __self.temp.raffle_timer_reset = __self.coin_response_reset; + } + __self.coin_response_timer = 3000; + __self.coin_response_reset = true; - // save previous raffle settings in case - // a new one is opened on accident - __self.raffle_restore_ticket_requests = __self.raffle_ticket_requests; - __self.raffle_restore_tickets = __self.raffle_tickets; + // save previous raffle settings in case + // a new one is opened on accident + __self.raffle_restore_ticket_requests = __self.raffle_ticket_requests; + __self.raffle_restore_tickets = __self.raffle_tickets; - // clear previous tickets - __self.raffle_ticket_requests = []; - __self.raffle_tickets = []; + // clear previous tickets + __self.raffle_ticket_requests = []; + __self.raffle_tickets = []; - // raffle open response - __self.irc.emit('message',{message:__self.pre_text + 'Raffle opened'}); - __self.irc.emit('message',{message:'+ Tickets cost ' + __self.raffle_ticket_cost + ' ' + __self.config.currency.toLowerCase() + ' / Maximum of ' + __self.raffle_max_tickets + ' tickets per viewer'}); + // raffle open response + __self.irc.emit('message',{message:__self.pre_text + 'Raffle opened'}); + __self.irc.emit('message',{message:'+ Tickets cost ' + __self.raffle_ticket_cost + ' ' + __self.config.currency.toLowerCase() + ' / Maximum of ' + __self.raffle_max_tickets + ' tickets per viewer'}); + } else { + // raffle in progress response + __self.irc.emit('message',{message:__self.pre_text + 'Raffle already in progress'}); + } } else { - // raffle in progress response - __self.irc.emit('message',{message:__self.pre_text + 'Raffle already in progress'}); + // auction in progress + __self.irc.emit('message', {message:__self.pre_text + 'You must close the auction before you can open an a raffle'}); } } else { - // auction in progress - __self.irc.emit('message', {message:__self.pre_text + 'You must close the auction before you can open an a raffle'}); + // gambling currently running + __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open a raffle'}); } - } else { - // gambling currently running - __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open a raffle'}); } break; case false: @@ -1497,6 +1552,274 @@ Currency.prototype.next_raffle_winner = function () { } }; +/* + * VOTING SYSTEM + * ------------ + */ +Currency.prototype.votes = function(status, data) { + var __self = this; + + switch(status){ + case true: + if (!__self.bets_status) { + if (!__self.auction_status) { + if (!__self.raffle_status) { + if (!__self.votes_status && !__self.votes_payout) { + var wager_msg = ''; + + __self.votes_status = true; + __self.votes_payout = true; + + __self.votes_board = []; + __self.votes_viewers = []; + __self.votes_total = 0; + + __self.votes_board = data.join().split(',').filter(function(n){return n}).slice(6).map(function(n){return {name: n, num: 0, total: 0};}); + for (var i = 0; i < __self.votes_board.length; i++) { + __self.votes_board[i].idx = i; + } + + for (var i = 0; i < __self.votes_board.length; i++) { + if (i !== __self.votes_board.length - 1) { + wager_msg += '"!' + __self.votes_board[i].name + '" / '; + } else { + wager_msg += '"!' + __self.votes_board[i].name + '"'; + } + } + + // output to chat + __self.irc.emit('message', {message:__self.pre_text + 'Voting is now open'}); + __self.irc.emit('message', {message:'+ Type ' + wager_msg + ' and the vote amount to enter'}); + } else { + if (__self.votes_payout) { + // payout pending message + __self.irc.emit('message', {message:__self.pre_text + 'Unable to take new votes until the last has been decided'}); + } else { + __self.irc.emit('message', {message:__self.pre_text + 'Voting already in progress'}); + } + } + // FIXME: messages below should be "cant open a vote until X is over/closed" instead? test it + } else { + // raffle in progress + __self.irc.emit('message', {message:__self.pre_text + 'Voting must be closed before you can open a raffle'}); + } + } else { + // auction currently running + __self.irc.emit('message', {message:__self.pre_text + 'Voting must be closed before you can open an auction'}); + } + } + break; + case false: + if (__self.votes_status && __self.votes_payout) { + __self.votes_status = false; + + __self.votes_deduct_votes(); + + // output to chat + if (__self.votes_viewers.length > 0) { + __self.irc.emit('message', {message:__self.pre_text + 'Voting is now closed'}); + for (var i = 0; i < __self.votes_board.length; i++) { + __self.irc.emit('message', {message:'+ ' + __self.votes_board[i].name + ' with ' + __self.votes_board[i].num + ' supporters totaling ' + __self.votes_board[i].total + ' votes'}); + } + + // + } else { + __self.irc.emit('message', {message:__self.pre_text + 'Voting closed, no votes were cast.'}); + } + } + break; + case 'pool': + function do_work() { + if (__self.votes_board.length > 0) { + __self.irc.emit('message', {message:__self.pre_text + 'Current voting pool is:'}); + for (var i = 0; i < __self.votes_board.length; i++) { + __self.irc.emit('message', {message:'+ ' + __self.votes_board[i].name + ' with ' + __self.votes_board[i].num + ' supporters totaling ' + __self.votes_board[i].total + ' votes'}); + } + } else { + __self.irc.emit('message', {message:__self.pre_text + 'No current vote.'}); + } + } + if (__self.votes_response_reset) { + __self.votes_response = setTimeout(function () {do_work();}, __self.votes_response_timer); + } else { + setTimeout(function () {do_work();}, __self.votes_response_timer); + } + break; + case 'winner': + if (!__self.votes_status) { + if (__self.votes_payout) { + for (var i = 0; i < __self.votes_board.length; i++) { + if (data == __self.votes_board[i].name) { + __self.votes_award_winner(__self.votes_board[i]); + __self.irc.emit('message', {message:__self.pre_text + 'Winner:' + data + 'had the most votes!'}); + } + } + } else { + __self.irc.emit('message', {message:__self.pre_text + 'A winner has already been decided.'}); + } + } else { + __self.irc.emit('message', {message:__self.pre_text + 'Voting must be closed before you can have a winner'}); + } + break; + case 'cancel': + if (!__self.votes_status) { + // Return all the currency back to original owners + if (__self.votes_payout) { + sql = ''; + for (var i = 0; i < __self.votes_viewers.length; i++) { + if (__self.votes_viewers[i].amount > 0) { + if (sql.length > 0) { + sql += '; '; + } + sql += 'UPDATE viewers '; + sql += 'SET points = points + ' + __self.votes_viewers[i].amount + ' '; + sql += 'WHERE user = \'' + __self.votes_viewers[i].viewer + '\''; + } + } + __self.db.execute(sql, function() { + __self.irc.emit('message', {message:__self.pre_text + 'Vote was canceled, all ' + __self.config.currency.toLowerCase() + ' returned.'}); + }); + __self.votes_payout = false; + } else { + __self.irc.emit('message', {message:__self.pre_text + 'No vote to cancel.'}); + } + } else { + __self.votes_status = false; + __self.votes_payout = false; + __self.irc.emit('message', {message:__self.pre_text + 'Current vote was canceled.'}); + } + break; + } +}; + +Currency.prototype.collect_votes = function (caller, vote, amount) { + var __self = this, has_vote = false; + + function do_work() { + var multi_response = []; + for (var i = 0; i < __self.votes_board.length; i++) { + multi_response.push([]); + } + + if (__self.votes_flood.length > 0) {// send flood requests + var vote; + // setup vote response + for (var i = 0; i < __self.votes_flood.length; i++) { + vote = __self.votes_viewers[__self.votes_flood[i]]; + multi_response[vote.vote].push(vote.viewer + ' (' + vote.amount + ')'); + + } + for (var i = 0; i < __self.votes_board.length; i++) { + if (multi_response[i].length > 0) { + __self.irc.emit('message', {message:'New votes for ' + __self.votes_board[i].name + ': ' + multi_response[i].join(', '), timer: 1}); + } + } + + } + + // clear flood requests + __self.votes_flood = []; + } + + // Bound amount by positive currency + __self.query_coins(caller, function (rows) { + for (var i = 0; i < rows.length; i++) { + amount = Math.min(amount, rows[i].points); + } + + if (__self.votes_viewers.length > 0) { + for (var i = 0; i < __self.votes_viewers.length; i++) { + if (__self.votes_viewers[i].viewer === caller) { + has_vote = true; + if (amount > __self.votes_viewers[i].amount) { + __self.votes_board[__self.votes_viewers[i].vote].num -= 1; + __self.votes_board[__self.votes_viewers[i].vote].total -= __self.votes_viewers[i].amount; + __self.votes_viewers[i].vote = vote.idx; + __self.votes_viewers[i].amount = amount; + __self.votes_board[vote.idx].num += 1; + __self.votes_board[vote.idx].total += amount; + // add flood users to array + if (__self.votes_flood.indexOf(i) < 0) { + __self.votes_flood.push(i); + } + } + break; + } + } + if (!has_vote && amount >= 1 && amount !== 0) { + __self.votes_viewers.push({viewer: caller, vote: vote.idx, amount: amount}); + __self.votes_board[vote.idx].num += 1; + __self.votes_board[vote.idx].total += amount; + __self.votes_flood.push(__self.votes_viewers.length - 1); + } + } else { + if (amount >= 1 && amount !== 0) { + __self.votes_viewers.push({viewer: caller, vote: vote.idx, amount: amount}); + __self.votes_board[vote.idx].num += 1; + __self.votes_board[vote.idx].total += amount; + __self.votes_flood.push(__self.votes_viewers.length - 1); + } + } + console.log(__self.votes_viewers); + + // check if flood has a set amount of requests and output + // if not, set the output timer + if (__self.votes_flood.length === __self.max_requests) { + do_work(); + } else { + if (__self.votes_response_reset) { + __self.votes_response = setTimeout(function () {do_work();}, __self.votes_response_timer); + } else { + setTimeout(function () {do_work();}, __self.votes_response_timer); + } + } + }); +}; + +Currency.prototype.votes_deduct_votes = function () { + var __self = this; + + if (__self.votes_viewers.length > 0) { + // Don't trust the on-the-fly numbers + for (var i = 0; i < __self.votes_board.length; i++) { + __self.votes_board[i].num = 0; + __self.votes_board[i].total = 0; + } + __self.votes_total = 0; + // Remove the points and add them to an escrow + sql = ''; + for (var i = 0; i < __self.votes_viewers.length; i++) { + if (__self.votes_viewers[i].amount > 0) { + if (sql.length > 0) { + sql += '; '; + } + sql += 'UPDATE viewers '; + sql += 'SET points = points - ' + __self.votes_viewers[i].amount + ' '; + sql += 'WHERE user = \'' + __self.votes_viewers[i].viewer + '\''; + __self.votes_board[__self.votes_viewers[i].vote].num += 1; + __self.votes_board[__self.votes_viewers[i].vote].total += __self.votes_viewers[i].amount; + __self.votes_total += __self.votes_viewers[i].amount; + } + } + __self.db.execute(sql, function() {}); + } +}; + +Currency.prototype.votes_award_winner = function (winner) { + var __self = this; + + if (__self.votes_payout) { + // set payout to complete + __self.votes_payout = false; + } + + // Clear the board + __self.votes_board = []; + __self.votes_viewers = []; + __self.votes_total = 0; +}; + + /** * ============================================ * BETTING SYSTEM diff --git a/lib/initialize.js b/lib/initialize.js index 970c635..2f92ae7 100644 --- a/lib/initialize.js +++ b/lib/initialize.js @@ -39,7 +39,7 @@ exports.initialize = function(options) { modpowers : config.currency.modpowers, ignorelist : config.ignorelist }); - web = require('./web.js')(db, { + web = require('./web.js')(db, currency, { port : config.web.port, title : config.twitch.channel, slogan : config.web.slogan, diff --git a/lib/web.js b/lib/web.js index bf93716..68c1985 100644 --- a/lib/web.js +++ b/lib/web.js @@ -2,10 +2,11 @@ var express = require('express'), https = require('https'); //---- Construct ---- -function WEB(db, options) { +function WEB(db, currency, options) { var __self = this; __self.db = db; + __self.currency = currency; __self.port = options.port || 9000; __self.title = options.title; @@ -38,6 +39,10 @@ WEB.prototype.start = function () { __self.db.execute(sql, function(rows) { var opts = __self.render_opts; opts.rows = rows; + opts.bet_status = __self.currency.bets_status; + opts.bet_board = __self.currency.bets_board; + opts.bet_viewers = __self.currency.bets_viewers; + res.render('index', opts); }); }); @@ -79,6 +84,6 @@ WEB.prototype.start = function () { }; -module.exports = function (db, options) { - return new WEB(db, options); +module.exports = function (db, currency, options) { + return new WEB(db, currency, options); }; diff --git a/web/templates/index.jade b/web/templates/index.jade index 8528665..c63eb6f 100755 --- a/web/templates/index.jade +++ b/web/templates/index.jade @@ -8,6 +8,27 @@ block content h2 #{title} is span#status offline. div#streambox + br + div#betstats + span.uk-text-bold Betting is currently: + #{bet_status} + if bet_status === true + span.uk-text-bold.uk-text-success  Active + else + span.uk-text-bold.uk-text-danger  Inactive + if bet_board + table.uk-table + caption Current Betting Pool + thead + tr + th Option + th Total + tbody + each option in bet_board + tr + td !#{option.name} + td #{option.total} + br div.uk-width-1-2 div.uk-panel.uk-panel-box table.uk-table.uk-table-hover.uk-table-striped @@ -33,6 +54,7 @@ block postscript $("#streambox").append("

");; } else { $("#streambox").append("

Offline

"); + $("#betstats").hide(); } }); }); diff --git a/web/templates/layout.jade b/web/templates/layout.jade index 304bf2b..e5f1e7e 100755 --- a/web/templates/layout.jade +++ b/web/templates/layout.jade @@ -34,7 +34,7 @@ html(lang="en") // bigimagelogo h1.uk-text-center #{title}
#{slogan} div.uk-width-1-4 - img.flip(src='/img/#{logo}') + img.flip.uk-float-right(src='/img/#{logo}') div.uk-width-1-1 nav.uk-navbar ul.uk-navbar-nav.uk-navbar-center