A Twitch.tv viewer reward and games system.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2248 lines
92 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
11 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
11 years ago
11 years ago
12 years ago
12 years ago
12 years ago
12 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
12 years ago
11 years ago
12 years ago
11 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
  1. /**
  2. * api:
  3. * Currency(irc object, database object, [required options])
  4. *
  5. * example:
  6. * var Currency = require('./lib/plugins/currency.js')(irc, db, {
  7. * currency : 'currency name',
  8. * subscribers : 'google doc spreadsheet until it's available from twitch api'
  9. * });
  10. *
  11. * commands:
  12. * !<currency>
  13. * reply with currency amount
  14. *
  15. * !<currency> on/[off repeat on/off]
  16. * toggle currency request status,
  17. * repeat status only available when
  18. * turning off requests
  19. *
  20. * !<currency> auction open
  21. * open a new auction
  22. *
  23. * !<currency> auction close
  24. * close current auction
  25. *
  26. * !<currency> auction cancel
  27. * cancel current auction
  28. *
  29. * !<currency> auction draw
  30. * draw the next highest bidder
  31. *
  32. * !bid <amount>
  33. * place a bid on an open auction,
  34. * only valid amounts will be accepted
  35. *
  36. * !<currency> raffle open <price> <max>
  37. * open a new raffle
  38. * price and max is optional
  39. * default price: 10
  40. * default max: 10
  41. *
  42. * !<currency> raffle close
  43. * draw the another ticket from raffle
  44. *
  45. * !<currency> raffle cancel
  46. * cancel the open raffle
  47. *
  48. * !<currency> raffle draw
  49. * open a new auction
  50. *
  51. * !<currency> raffle restore
  52. * restores a previous raffle if a new
  53. * one is accidentally opened
  54. *
  55. * !ticket <amount>
  56. * place a bid on an open auction,
  57. * only valid amounts will be accepted
  58. */
  59. var https = require('https'),
  60. http = require('http'),
  61. querystr = require('querystring'),
  62. file = require('fs'),
  63. utils = require('./utils.js');
  64. //-------- Construct ---------
  65. function Currency(irc, db, commands, options) {
  66. var __self = this;
  67. __self.irc = irc;
  68. __self.db = db;
  69. __self.custom_commands = commands;
  70. // config
  71. __self.config = options || {};
  72. __self.config.currency = options.currency || 'coins';
  73. __self.config.subscribers_json = options.subscribers || '';
  74. __self.config.website = options.website || '';
  75. __self.config.modpowers = options.modpowers || false;
  76. __self.config.ignorelist = options.ignorelist || ['jtv'];
  77. __self.config.exchanges = options.exchanges || {};
  78. // general settings
  79. __self.pre_text = '> ' + __self.config.currency + ': ';
  80. __self.max_requests = 10;//response after 10 request
  81. __self.temp = {};
  82. // currency request settings
  83. __self.coin_flood = [];
  84. __self.coin_response = null;
  85. __self.coin_response_timer = 3000;
  86. __self.coin_response_reset = true;
  87. __self.coin_toggle = false;
  88. __self.coin_toggle_msg = null;
  89. __self.coin_toggle_timer = 180000;//milliseconds
  90. __self.top_flood = [];
  91. __self.top_response_reset = true;
  92. __self.rank_flood = [];
  93. __self.rank_response_reset = true;
  94. // auction settings
  95. __self.auction_status = false;
  96. __self.auction_bids = [];
  97. __self.auction_previous_bid = {};
  98. __self.auction_bid_response = null;
  99. __self.auction_bid_response_long = null;
  100. // handout coins settings
  101. __self.viewer_list = [];
  102. __self.streaming = false;
  103. __self.streaming_check = 4;//minutes
  104. __self.give_coins = false;
  105. __self.give_coins_timer = options.payrate || 30;//minutes
  106. __self.subscriber_check = 4;//minutes
  107. __self.subscribers = [];
  108. // raffle settings
  109. __self.raffle_status = false;
  110. __self.raffle_ticket_requests = [];
  111. __self.raffle_tickets = [];
  112. __self.raffle_ticket_cost = 10;//currency cost per ticket
  113. __self.raffle_max_tickets = 10;
  114. // raffle restoration
  115. __self.raffle_restore_ticket_requests = [];
  116. __self.raffle_restore_tickets = [];
  117. // gambling settings
  118. __self.bets_status = false;
  119. __self.bets_board = [];
  120. __self.bets_viewers = [];
  121. __self.bets_payout = false;
  122. __self.bets_total = 0;
  123. __self.bets_flood = [];
  124. __self.bets_response = null;
  125. __self.bets_response_timer = 3000;
  126. __self.bets_response_reset = true;
  127. // voting settings
  128. __self.votes_status = false;
  129. __self.votes_board = [];
  130. __self.votes_viewers = [];
  131. __self.votes_payout = false;
  132. __self.votes_total = 0;
  133. __self.votes_flood = [];
  134. __self.votes_response = null;
  135. __self.votes_response_timer = 3000;
  136. __self.votes_response_reset = true;
  137. // high score table
  138. __self.score_bet_pool = 0;
  139. __self.score_bet_single = 0;
  140. // commands add/rem settings
  141. __self.command_add_string = options.add_command || 'addcmd';
  142. __self.command_remove_string = options.remove_command || 'removecmd';
  143. // message to new subscribers
  144. __self.sub_ty_msg = options.sub_ty_msg || '';
  145. // stored twitch stream data
  146. }
  147. //-------- Methods ---------
  148. Currency.prototype.start = function () {
  149. var __self = this;
  150. // populate highscore holders
  151. __self.db.execute('SELECT * FROM highscores', function(rows) {
  152. rows.forEach(function(element, index, array){
  153. switch(element.name) {
  154. case 'bet_pool':
  155. __self.score_bet_pool = element.value;
  156. break;
  157. case 'bet_single':
  158. __self.score_bet_single = element.value;
  159. break;
  160. }
  161. });
  162. });
  163. __self.handout_coins();
  164. };
  165. Currency.prototype.commands = function (data) {
  166. var __self = this,
  167. broadcaster_bot_initiated = __self.irc.caller(data[0]).toLowerCase() === __self.irc.config.broadcaster.toLowerCase() || __self.irc.caller(data[0]).toLowerCase() === __self.irc.config.name.toLowerCase(),
  168. moderator_initiated = __self.irc.mods.indexOf(__self.irc.caller(data[0])) >= 0;
  169. // handle !<currency> commands
  170. if (data[3].slice(1) === '!' + __self.config.currency.toLowerCase()) {
  171. // public commands
  172. if (!__self.coin_toggle && data[4] === undefined) {
  173. __self.get_coins(__self.irc.caller(data[0]));
  174. }
  175. // broadcaster only commands
  176. if (broadcaster_bot_initiated || (__self.config.modpowers && moderator_initiated)) {
  177. //add / remove custom commands
  178. if (data[4] === __self.command_add_string && data[5] !== undefined && data[6] !== undefined) {
  179. var command = data[5];
  180. var text = data.slice(6);
  181. text = text.join(' ');
  182. __self.custom_commands.add(command, text);
  183. }
  184. if (data[4] === __self.command_remove_string && data[5] !== undefined) {
  185. var command = data[5];
  186. __self.custom_commands.remove(command);
  187. }
  188. //open / close auction system
  189. if (data[4] === 'auction') {
  190. switch (data[5]) {
  191. case 'open':
  192. __self.auction(true);
  193. break;
  194. case 'close':
  195. __self.auction(false);
  196. break;
  197. case 'draw':
  198. __self.next_auction_winner();
  199. break;
  200. case 'cancel':
  201. __self.auction('cancel');
  202. break;
  203. case 'help':
  204. __self.irc.emit('message', {message:__self.pre_text + '!' + __self.config.currency.toLowerCase() + ' auction open/close/cancel/draw'});
  205. break;
  206. }
  207. }
  208. // open / close raffle system
  209. if (data[4] === 'raffle') {
  210. switch (data[5]) {
  211. case 'open':
  212. if (data[6] && data[7] && !__self.raffle_status) {
  213. if(parseInt(data[6], 10) >= 0 && parseInt(data[7], 10) > 0) {
  214. // save default values
  215. __self.temp.raffle_ticket_cost = __self.raffle_ticket_cost;
  216. __self.temp.raffle_max_tickets = __self.raffle_max_tickets;
  217. // set new raffle cost / amount
  218. __self.raffle_ticket_cost = data[6];
  219. __self.raffle_max_tickets = data[7];
  220. }
  221. } else if (__self.temp.raffle_ticket_cost && __self.temp.raffle_max_tickets && !__self.raffle_status){
  222. __self.raffle_ticket_cost = __self.temp.raffle_ticket_cost;
  223. __self.raffle_max_tickets = __self.temp.raffle_max_tickets;
  224. delete __self.temp.raffle_ticket_cost;
  225. delete __self.temp.raffle_max_tickets;
  226. }
  227. __self.raffle(true);
  228. break;
  229. case 'close':
  230. __self.raffle(false);
  231. break;
  232. case 'draw':
  233. __self.next_raffle_winner();
  234. break;
  235. case 'cancel':
  236. __self.raffle('cancel');
  237. break;
  238. case 'restore':
  239. __self.raffle('restore');
  240. break;
  241. case 'help':
  242. __self.irc.emit('message', {message:__self.pre_text + '!' + __self.config.currency.toLowerCase() + ' raffle open <ticket cost> <tickets per user>/close/cancel/draw/restore'});
  243. break;
  244. }
  245. }
  246. // open / close betting system
  247. if (data[4] === 'bet') {
  248. switch (data[5]) {
  249. case 'open':
  250. if (data[6] && data[7]) {
  251. __self.bets(true, data);
  252. } else {
  253. __self.irc.emit('message', {message:__self.pre_text + 'Unable to open betting, need at least two items to bet against'});
  254. }
  255. break;
  256. case 'close':
  257. __self.bets(false, null);
  258. break;
  259. case 'pool':
  260. __self.bets('pool', null);
  261. break;
  262. case 'winner':
  263. __self.bets('winner', data[6]);
  264. break;
  265. case 'cancel':
  266. __self.bets('cancel');
  267. break;
  268. case 'help':
  269. __self.irc.emit('message', {message:__self.pre_text + '!' + __self.config.currency.toLowerCase() + ' bet open <option A> <option B> <etc>/close/pool/winner <option>/cancel'});
  270. break;
  271. }
  272. }
  273. // open / close voting system
  274. if (data[4] === 'vote') {
  275. switch (data[5]) {
  276. case 'open':
  277. if (data[6] && data[7]) {
  278. __self.votes(true, data);
  279. } else {
  280. __self.irc.emit('message', {message:__self.pre_text + 'Unable to open voting, need at least two items to vote for'});
  281. }
  282. break;
  283. case 'close':
  284. __self.votes(false, null);
  285. break;
  286. case 'pool':
  287. __self.votes('pool', null);
  288. break;
  289. case 'winner':
  290. __self.votes('winner', data[6]);
  291. break;
  292. case 'cancel':
  293. __self.votes('cancel');
  294. break;
  295. }
  296. }
  297. // TODO: create a log file that monitors just add/remove/push - greedy mods :D
  298. // add currency
  299. if (data[4] === 'add') {
  300. if(parseInt(data[5], 10) > 0 && data[6]) {
  301. __self.adjust_currency('add', data[5], data[6]);
  302. }
  303. }
  304. // remove currency
  305. if (data[4] === 'remove') {
  306. if(parseInt(data[5], 10) > 0 && data[6]) {
  307. __self.adjust_currency('remove', data[5], data[6]);
  308. }
  309. }
  310. // push currency to new viewer
  311. if (data[4] === 'push') {
  312. if(parseInt(data[5], 10) > 0 && data[6]) {
  313. __self.adjust_currency('push', data[5], data[6]);
  314. }
  315. }
  316. }
  317. // moderator commands
  318. if (broadcaster_bot_initiated || moderator_initiated) {
  319. // enable/disable currency requests
  320. switch (data[4]) {
  321. case 'status':
  322. if (__self.streaming) {
  323. __self.irc.emit('message', {message:__self.pre_text + "Stream is online, " + __self.config.currency + " are being distributed!"});
  324. } else {
  325. __self.irc.emit('message', {message:__self.pre_text + "Stream is offline :("});
  326. }
  327. break;
  328. case 'on':
  329. if (!__self.auction_status && !__self.raffle_status) {
  330. __self.coin_toggle = false;
  331. // output currency request status
  332. __self.irc.emit('message', {message:__self.pre_text + 'Currency requests are now enabled. Type !' + __self.config.currency.toLowerCase() + ' to view your total'});
  333. // stop periodic message
  334. clearInterval(__self.coin_toggle_msg);
  335. }
  336. break;
  337. case 'off':
  338. if (!__self.auction_status && !__self.raffle_status) {
  339. var msg;
  340. if (!__self.coin_toggle) {
  341. // output message depending on if an offsite is provided
  342. if (__self.config.website !== '') {
  343. msg = __self.pre_text + 'Currency requests have been disabled. To view your ' + __self.config.currency + ' please visit ' + __self.config.website;
  344. __self.irc.emit('message', {message:msg});
  345. } else {
  346. msg = __self.pre_text + 'Currency requests have been disabled';
  347. __self.irc.emit('message', {message:msg});
  348. }
  349. }
  350. // start the periodic message
  351. if (data[5] !== undefined && data[6] !== undefined) {
  352. // manually enable / disable repeat
  353. if (data[5] === 'repeat') {
  354. switch (data[6]) {
  355. case 'on':
  356. __self.irc.emit('message', {message:'+ Periodic notification enabled'});
  357. __self.coin_toggle_msg = setInterval(function () {
  358. if (__self.coin_toggle) {
  359. msg = __self.pre_text + 'To view your ' + __self.config.currency + ' please visit ' + __self.config.website;
  360. __self.irc.emit('message', {message:msg});
  361. }
  362. }, __self.coin_toggle_timer);
  363. break;
  364. case 'off':
  365. __self.irc.emit('message', {message:'+ Periodic notification disabled'});
  366. clearInterval(__self.coin_toggle_msg);
  367. }
  368. }
  369. }
  370. __self.coin_toggle = true;
  371. }
  372. break;
  373. }
  374. // adjust currency response rate
  375. if (data[4] === 'timer') {
  376. if (isNaN(parseInt(data[5], 10)) === false) {
  377. if (data[5] >= 3 && data[5] % 1 === 0) {
  378. __self.coin_response_timer = data[5] * 1000;
  379. __self.irc.emit('message', {message:__self.pre_text + 'Currency totals will now show ' + data[5] + ' seconds after request'});
  380. if (data[6] && data[7]) {
  381. if (data[6] === 'reset') {
  382. switch(data[7]) {
  383. case 'on':
  384. __self.irc.emit('message', {message:'+ Timer will now reset after each new request'});
  385. __self.coin_response_reset = true;
  386. break;
  387. case 'off':
  388. __self.irc.emit('message', {message:'+ Timer will not reset after each new request'});
  389. __self.coin_response_reset = false;
  390. break;
  391. }
  392. }
  393. }
  394. } else if (data[5] < 3) {
  395. __self.irc.emit('message', {message:__self.pre_text + 'Timer cannot be less than 2 seconds'});
  396. }
  397. }
  398. }
  399. }
  400. }
  401. // public commands related to !<currency>
  402. switch (data[3].slice(1)) {
  403. // submit bid for the auction
  404. case '!bid':
  405. if (isNaN(parseInt(data[4], 10)) === false) {
  406. if (data[4] > 0 && data[4] % 1 === 0) {
  407. __self.bid(__self.irc.caller(data[0]), parseInt(data[4], 10));
  408. }
  409. }
  410. break;
  411. // purchase a ticket for raffle
  412. case '!ticket':
  413. if (isNaN(parseInt(data[4], 10)) === false) {
  414. if (data[4] >= 0 && data[4] % 1 === 0) {
  415. __self.collect_tickets(__self.irc.caller(data[0]), parseInt(data[4], 10));
  416. }
  417. }
  418. break;
  419. // show top currency earners
  420. case '!top':
  421. __self.get_top(data[4]);
  422. break;
  423. // show a users ranking in the ladder
  424. case '!rank':
  425. __self.get_rank(__self.irc.caller(data[0]).toLowerCase());
  426. break;
  427. case '!exchange':
  428. if(Object.keys(__self.config.exchanges).length != 0)
  429. __self.currency_exchange(__self.irc.caller(data[0]), parseInt(data[4], 10), data[5]);
  430. break;
  431. case '!addquote':
  432. __self.add_quotation(data);
  433. break;
  434. case '!delquote':
  435. __self.remove_quotation(data);
  436. break;
  437. case '!quote':
  438. __self.get_quotation();
  439. }
  440. // place a bet
  441. if (__self.bets_status === true) {
  442. for (var i = 0; i < __self.bets_board.length; i++) {
  443. if (data[3].slice(1) === '!' + __self.bets_board[i].name) {
  444. if (isNaN(parseInt(data[4], 10)) === false) {
  445. if (data[4] >= 0 && data[4] % 1 === 0) {
  446. __self.collect_bets(__self.irc.caller(data[0]), __self.bets_board[i], parseInt(data[4], 10));
  447. break;
  448. }
  449. }
  450. }
  451. }
  452. }
  453. // cast a vote
  454. if (__self.votes_status === true) {
  455. for (var i = 0; i < __self.votes_board.length; i++) {
  456. if (data[3].slice(1) === '!' + __self.votes_board[i].name) {
  457. if (isNaN(parseInt(data[4], 10)) === false) {
  458. if (data[4] >= 0 && data[4] % 1 === 0) {
  459. __self.collect_votes(__self.irc.caller(data[0]), __self.votes_board[i], parseInt(data[4], 10));
  460. break;
  461. }
  462. }
  463. }
  464. }
  465. }
  466. };
  467. /**
  468. * ============================================
  469. * CURRENCY REQUESTS
  470. * --------------------------------------------
  471. */
  472. Currency.prototype.get_coins = function (caller) {
  473. var __self = this;
  474. function fill_request(viewer, points) {
  475. var request = '(' + points + ')';
  476. if (__self.raffle_status) {
  477. for (var i = 0; i < __self.raffle_ticket_requests.length; i++){
  478. if (__self.raffle_ticket_requests[i].viewer.toLowerCase() === viewer.toLowerCase() && (__self.raffle_ticket_requests[i].tickets * __self.raffle_ticket_cost) <= points) {
  479. request = '(' + (points - (__self.raffle_ticket_requests[i].tickets * __self.raffle_ticket_cost)) + ') [' + __self.raffle_ticket_requests[i].tickets + ']';
  480. break;
  481. }
  482. }
  483. return request;
  484. } else {
  485. return request;
  486. }
  487. }
  488. function do_work() {
  489. var multi_response = '';
  490. if (__self.coin_flood.length > 1) {// send flood requests
  491. __self.query_coins(__self.coin_flood, function (rows) {
  492. for (var i = 0; i < rows.length; i++) {
  493. var currency_request = fill_request(rows[i].user, rows[i].points);
  494. // setup currency response
  495. if (i !== rows.length - 1) {
  496. multi_response += rows[i].user + ' ' + currency_request + ', ';
  497. } else {
  498. multi_response += rows[i].user + ' ' + currency_request;
  499. }
  500. }
  501. __self.irc.emit('message', {message:__self.pre_text + multi_response, timer: 1});
  502. });
  503. } else if (__self.coin_flood.length === 1) {// send single request
  504. __self.query_coins(caller, function (rows) {
  505. var currency_request = fill_request(rows[0].user, rows[0].points);
  506. __self.irc.emit('message', {message:__self.pre_text + caller + ' ' + currency_request, timer: 1});
  507. });
  508. }
  509. // clear flood requests
  510. __self.coin_flood = [];
  511. }
  512. // add flood users to array
  513. if (__self.coin_flood.indexOf(caller) < 0) {
  514. __self.coin_flood.push(caller);
  515. }
  516. // clear timer on flood
  517. if (__self.coin_response_reset) {
  518. clearTimeout(__self.coin_response);
  519. }
  520. // check if flood has a set amount of requests and output
  521. // if not, set the output timer
  522. if (__self.coin_flood.length === __self.max_requests) {
  523. do_work();
  524. } else {
  525. if (__self.coin_response_reset) {
  526. __self.coin_response = setTimeout(function () {do_work();}, __self.coin_response_timer);
  527. } else {
  528. setTimeout(function () {do_work();}, __self.coin_response_timer);
  529. }
  530. }
  531. };
  532. Currency.prototype.currency_exchange = function (name, amount, type) {
  533. console.log('INSIDE CURR EXCHANGE');
  534. var __self = this,
  535. rate = 15,
  536. cost = (amount * (rate / 100))
  537. total = amount + cost;
  538. if( name && amount && type ) {
  539. console.log('ready to post');
  540. if(Object.keys(__self.config.exchanges).indexOf(type) > -1) {
  541. __self.query_coins(name.toLowerCase(), function (rows) {
  542. var message = '',
  543. response = new String();
  544. if(rows[0].points >= total) {
  545. // Prepare for POST
  546. var data = querystr.stringify({'name': name, 'amount': amount});
  547. var opts = {
  548. port: __self.config.exchanges[type],
  549. path: '/api/exchange',
  550. method: 'POST',
  551. headers: {
  552. 'Content-Type': 'application/x-www-form-urlencoded',
  553. 'Content-Length': Buffer.byteLength(data)
  554. }
  555. };
  556. var request = http.request(opts, function(res) {
  557. res.setEncoding('utf-8');
  558. res.on('data', function(chunk) {
  559. response += chunk;
  560. });
  561. res.on('end', function() {
  562. if(response == '1') {
  563. // Exchanged succesfully, let's remove local currency
  564. __self.adjust_currency('remove', total, name);
  565. message = name+' exchanged '+amount+' for '+type+'. Cost '+total;
  566. __self.irc.emit('message', {message:__self.pre_text + message, timer: 1});
  567. }
  568. });
  569. });
  570. request.write(data);
  571. request.end();
  572. }
  573. });
  574. }
  575. } else {
  576. message = '!exchange <amount> <type> [Valid types: ';
  577. keys = Object.keys(__self.config.exchanges);
  578. keys.forEach(function(element, index, array) {
  579. message += keys[index]+', ';
  580. });
  581. message+=']';
  582. __self.irc.emit('message', {message:__self.pre_text + message, timer: 1});
  583. }
  584. };
  585. Currency.prototype.get_rank = function (data) {
  586. var __self = this;
  587. function do_work() {
  588. __self.query_rank(__self.rank_flood, function (ranks, total) {
  589. var response = '> Rank (of '+total+'): ';
  590. for(var i = 0; i < ranks.length; i++) {
  591. response += ranks[i]['user'] + ' ('+ranks[i]['@row_number:=@row_number+1']+') ['+ranks[i]['points']+'], ';
  592. }
  593. response = response.slice(1,-2);
  594. __self.irc.emit('message', {message: response, timer: 1});
  595. });
  596. __self.rank_flood = [];
  597. }
  598. // add flood users to array
  599. if (__self.rank_flood.indexOf(data) < 0) {
  600. __self.rank_flood.push(data);
  601. }
  602. // clear timer on flood
  603. if (__self.rank_response_reset) {
  604. clearTimeout(__self.rank_response);
  605. }
  606. // check if flood has hit max requests or set timer
  607. if (__self.rank_flood.length === __self.max_requests) {
  608. do_work();
  609. } else {
  610. if (__self.rank_response_reset) {
  611. __self.rank_response = setTimeout(function () {do_work();}, __self.coin_response_timer);
  612. } else {
  613. setTimeout(function () {do_work();}, __self.coin_response_timer);
  614. }
  615. }
  616. };
  617. Currency.prototype.get_top = function (data) {
  618. var __self = this;
  619. var limit = 5;
  620. function do_work() {
  621. var response = '';
  622. __self.query_top(__self.top_flood, function (rows) {
  623. for (var i = 0; i < rows.length; i++) {
  624. // setup response
  625. if (i !== rows.length - 1) {
  626. response += '#'+(i+1)+': ' + rows[i].user + '('+rows[i].points+'), ';
  627. } else {
  628. response += '#'+(i+1)+': ' + rows[i].user + '('+rows[i].points+')';
  629. }
  630. }
  631. __self.irc.emit('message', {message:'> Top Farmers: ' + response});
  632. });
  633. // clear flood requests
  634. __self.top_flood = [];
  635. }
  636. // add flood users to array
  637. if (__self.top_flood.indexOf(limit) < 0) {
  638. __self.top_flood.push(limit);
  639. }
  640. // clear timer on flood
  641. if (__self.top_response_reset) {
  642. clearTimeout(__self.top_response);
  643. }
  644. // check if flood has hit max requests or set timer
  645. if (__self.top_flood.length === __self.max_requests) {
  646. do_work();
  647. } else {
  648. if (__self.top_response_reset) {
  649. __self.top_response = setTimeout(function () {do_work();}, __self.coin_response_timer);
  650. } else {
  651. setTimeout(function () {do_work();}, __self.coin_response_timer);
  652. }
  653. }
  654. };
  655. Currency.prototype.query_rank = function(data, callback) {
  656. var __self = this;
  657. var ranks = {}, total = 0;
  658. var inner_sql = '', sql = 'set @row_number:=0;';
  659. data.forEach(function(user) {
  660. inner_sql += "user='"+user+"' or ";
  661. });
  662. // remove last or
  663. inner_sql=inner_sql.slice(0,-4);
  664. sql += "select * from (select user, points, @row_number:=@row_number+1 from viewers order by points desc) as rank where ("+inner_sql+");select count(1) from viewers where points > 0;";
  665. __self.db.execute(sql, function(rows) {
  666. total = rows[2][0]['count(1)'];
  667. ranks = rows[1];
  668. callback(ranks, total);
  669. });
  670. };
  671. Currency.prototype.query_top = function (data, callback) {
  672. var __self = this, sql = '';
  673. // build sql conditions
  674. sql = 'SELECT * FROM viewers ORDER BY points DESC LIMIT ' + data + ';';
  675. __self.db.execute(sql, function(rows) {
  676. callback(rows);
  677. });
  678. };
  679. Currency.prototype.query_coins = function (data, callback) {
  680. var __self = this, sql = '';
  681. // build sql conditions
  682. if (typeof data === 'string') {
  683. sql = 'SELECT * FROM viewers WHERE ' + 'user = \'' + data.toLowerCase() + '\'';
  684. } else {
  685. for (var i = 0; i < data.length; i++) {
  686. if (i !== data.length - 1) {
  687. sql += 'SELECT * FROM viewers WHERE ' + 'user = \'' + data[i].toLowerCase() + '\'' + ';';
  688. } else {
  689. sql += 'SELECT * FROM viewers WHERE ' + 'user = \'' + data[i].toLowerCase() + '\'';
  690. }
  691. }
  692. }
  693. // execute query
  694. __self.db.execute(sql, function (rows) {
  695. var temp = [], newrows = [];
  696. if (typeof data !== 'string') {
  697. // get rid of the nested arrays
  698. for (var i = 0; i < rows.length; i++) {
  699. if (rows[i].length > 0) {
  700. newrows.push(rows[i][0]);
  701. }
  702. }
  703. // separate users into their own array
  704. for (var i = 0; i < newrows.length; i++) {
  705. temp.push(newrows[i].user.charAt(0).toUpperCase() + newrows[i].user.slice(1));
  706. }
  707. // compare the users in the data array against the temp array
  708. // if not found, push them to rows with 0 points
  709. for (var i = 0; i < data.length; i++) {
  710. if (temp.indexOf(data[i]) < 0) {
  711. newrows.push({'user' : data[i], 'points' : 0});
  712. }
  713. }
  714. // capitalize usernames on rows
  715. for (var key in newrows) {
  716. if (newrows.hasOwnProperty(key)) {
  717. newrows[key].user = newrows[key].user.charAt(0).toUpperCase() + newrows[key].user.slice(1);
  718. }
  719. }
  720. rows = newrows;
  721. } else {
  722. if (rows.length === 0) {
  723. rows = [{'user' : data, 'points' : 0}];
  724. } else {
  725. rows[0].user = rows[0].user.charAt(0).toUpperCase() + rows[0].user.slice(1);
  726. }
  727. }
  728. callback(rows);
  729. });
  730. };
  731. /**
  732. * ============================================
  733. * HANDOUT CURRENCY
  734. * --------------------------------------------
  735. */
  736. Currency.prototype.handout_coins = function () {
  737. var __self = this;
  738. // check stream status
  739. function stream_status() {
  740. var time = utils.make_interval(__self.streaming_check);
  741. if (time === 0) {
  742. // get stream status
  743. https.get('https://api.twitch.tv/kraken/streams/' + __self.irc.config.channel.slice(1), function (response) {
  744. var body = '';
  745. // put together response
  746. response.on('data', function (chunk) {
  747. body += chunk;
  748. });
  749. // start / stop handing out coins based on stream status
  750. response.on('end', function () {
  751. var json = JSON.parse(body);
  752. __self.streaming = json.stream !== null;
  753. if (__self.streaming && __self.give_coins === false) {
  754. insert_coins();
  755. }
  756. __self.irc.emit('data', 'DATA - Online Status Check - Returned: ' + __self.streaming);
  757. setTimeout(stream_status, 1000);
  758. });
  759. });
  760. } else {
  761. setTimeout(stream_status, time);
  762. }
  763. }
  764. // get subscribers
  765. function subscribers() {
  766. var time = utils.make_interval(__self.subscriber_check);
  767. if (time === 0) {
  768. // get stream status
  769. http.get(__self.config.subscribers_json, function (response) {
  770. var body = '';
  771. // put together response
  772. response.on('data', function (chunk) {
  773. body += chunk;
  774. });
  775. // start / stop handing out coins based on stream status
  776. response.on('end', function () {
  777. var json = JSON.parse(body);
  778. var entries = json.feed.entry, subs = '';
  779. __self.subscribers = [];
  780. for (var i = 0; i < entries.length; i++) {
  781. __self.subscribers.push(entries[i].title['$t']);
  782. subs += entries[i].title['$t'] + ' ';
  783. }
  784. __self.irc.emit('data', 'DATA - Subscriber Check - Returned: ' + subs);
  785. setTimeout(subscribers, 1000);
  786. });
  787. });
  788. } else {
  789. setTimeout(subscribers, time);
  790. }
  791. }
  792. // trigger coin handout
  793. function insert_coins() {
  794. __self.give_coins = __self.streaming;
  795. console.log('DATA - Inserting coins');
  796. if (__self.give_coins) {
  797. var time = utils.make_interval(__self.give_coins_timer);
  798. if (time === 0) {
  799. // build sql from the saved viewer list
  800. var sql = '';
  801. for (var i = 0; i < __self.viewer_list.length; i++) {
  802. var currency_amount = __self.subscribers.indexOf(__self.viewer_list[i]) >= 0 ? 2 : 1;
  803. if (__self.viewer_list[i] !== '') {
  804. if (i != __self.viewer_list.length - 1) {
  805. sql += 'INSERT INTO viewers (user, points) ';
  806. sql += 'VALUES (\'' + __self.viewer_list[i] + '\', ' + currency_amount + ') ';
  807. sql += 'ON DUPLICATE KEY UPDATE points = points + ' + currency_amount + '; ';
  808. } else {
  809. sql += 'INSERT INTO viewers (user, points) ';
  810. sql += 'VALUES (\'' + __self.viewer_list[i] + '\', ' + currency_amount + ') ';
  811. sql += 'ON DUPLICATE KEY UPDATE points = points + ' + currency_amount;
  812. }
  813. }
  814. }
  815. // execute query
  816. __self.db.execute(sql, function () {});
  817. setTimeout(insert_coins, 1000);
  818. } else {
  819. setTimeout(insert_coins, time);
  820. }
  821. }
  822. }
  823. // monitor viewers in irc
  824. __self.irc.on('data', function (data) {
  825. var data_split = data.split(' '), viewer = '';
  826. // viewers from \who
  827. // TODO: new twitch chat doesn't allow /who, remove this?
  828. if (data_split[3] == '352') {
  829. if (data_split[6] !== undefined) {
  830. viewer = data_split[6].toLowerCase();
  831. if (__self.viewer_list.indexOf(viewer) < 0 && __self.config.ignorelist.indexOf(viewer) < 0) {
  832. __self.viewer_list.push(viewer);
  833. }
  834. }
  835. }
  836. // Processing below replaces the now defunct who-calls above
  837. // handle /names
  838. if (data_split[3] == '353'){
  839. viewers = data_split.splice(7);
  840. viewers.forEach(function(name) {
  841. if(name.charAt(0) === ':') name = name.slice(1);
  842. if (__self.viewer_list.indexOf(name) < 0 && __self.config.ignorelist.indexOf(name) < 0) {
  843. __self.viewer_list.push(name);
  844. }
  845. });
  846. }
  847. // handle JOIN's
  848. if (data_split[3] == 'JOIN') {
  849. var servernick = data_split[2].toLowerCase().split('!');
  850. viewer = servernick[0];
  851. if (viewer != __self.irc.config.name.toLowerCase()) {
  852. viewer = viewer.slice(1);
  853. }
  854. if (__self.viewer_list.indexOf(viewer) < 0 && __self.config.ignorelist.indexOf(viewer) < 0) {
  855. __self.viewer_list.push(viewer);
  856. } else {
  857. }
  858. }
  859. // handle PART's
  860. if (data_split[3] == 'PART') {
  861. var servernick = data_split[2].toLowerCase().split('!');
  862. viewer = servernick[0];
  863. if (viewer != __self.irc.config.name.toLowerCase()) {
  864. viewer = viewer.slice(1);
  865. }
  866. var viewer_idx = __self.viewer_list.indexOf(viewer);
  867. if (viewer_idx >= 0) {
  868. __self.viewer_list.splice(viewer_idx,1);
  869. } else {
  870. }
  871. }
  872. // viewers chatting
  873. if (data_split[3] == 'PRIVMSG') {
  874. var servernick = data_split[2].toLowerCase().split('!');
  875. viewer = servernick[0];
  876. if (viewer != __self.irc.config.name.toLowerCase()) {
  877. viewer = viewer.slice(1);
  878. }
  879. // add missing chatters to viewer_list
  880. if (__self.viewer_list.indexOf(viewer) < 0 && __self.config.ignorelist.indexOf(viewer) < 0) {
  881. __self.viewer_list.push(viewer);
  882. console.log("[*ADD NAME*] "+viewer+" from CHAT");
  883. }
  884. // is this a new subscription notice?
  885. if(viewer == 'twitchnotify') {
  886. var subscriber = data_split[5].slice(1);
  887. __self.irc.emit('message', {message:'> SUBHYPE: Thank you ' + subscriber + '. ' + __self.sub_ty_msg});
  888. }
  889. // log chat if enabled
  890. if (__self.irc.store_chat && __self.config.ignorelist.indexOf(viewer) < 0) {
  891. file.exists(__self.irc.chat_log, function (exists) {
  892. if (exists) {
  893. // get a timestamp
  894. var date = new Date(),
  895. hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(),
  896. min = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(),
  897. sec = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(),
  898. streaming_time = '';
  899. // create start time string
  900. streaming_time += hours.toString() + ':' + min.toString();
  901. // craft message
  902. data_split[5] = data_split[5].slice(1);
  903. var message = data_split.slice(5).join(' ');
  904. // put it all together
  905. var logline = '[' + streaming_time + '] <' + viewer + '> ' + message + '\r\n';
  906. file.appendFile(__self.irc.chat_log, logline, 'utf-8', function (err) {
  907. if (err) {
  908. throw err;
  909. }
  910. });
  911. }
  912. });
  913. }
  914. }
  915. });
  916. stream_status();
  917. // only start subscribers if gdoc is available
  918. if (__self.config.subscribers_json !== '') {
  919. subscribers();
  920. }
  921. };
  922. /**
  923. * ============================================
  924. * Adjust Currency
  925. * --------------------------------------------
  926. */
  927. Currency.prototype.adjust_currency = function (method, amount, viewer) {
  928. var __self = this;
  929. viewer = viewer.toLowerCase();
  930. __self.db.execute('SELECT * FROM viewers WHERE user=\'' + viewer + '\'', function(rows){
  931. if (rows.length > 0 || method === 'push') {
  932. var check = rows.length > 0 ? rows[0].user : rows.push({user: viewer});
  933. if (check === viewer || method === 'push') {
  934. var sql = '', settings = [];
  935. // push settings for message
  936. if (method === 'add' || method === 'push') {
  937. settings.push('+');
  938. settings.push('Added');
  939. settings.push('to');
  940. } else if (method === 'remove') {
  941. settings.push('-');
  942. settings.push('Removed');
  943. settings.push('from');
  944. }
  945. settings.push(rows[0].user.charAt(0).toUpperCase() + rows[0].user.slice(1));
  946. // create sql
  947. if (method === 'add' || method === 'remove') {
  948. sql += 'UPDATE viewers ';
  949. sql += 'SET points = points ' + settings[0] + ' ' + amount + ' ';
  950. sql += 'WHERE user = \'' + rows[0].user + '\'; ';
  951. } else if (method === 'push') {
  952. sql += 'INSERT INTO viewers (user, points) ';
  953. sql += 'VALUES (\'' + viewer + '\', ' + amount + ') ';
  954. sql += 'ON DUPLICATE KEY UPDATE points = points + ' + amount + '; ';
  955. }
  956. //execute adjustment
  957. __self.db.execute(sql, function(){
  958. __self.irc.emit('message', {message:__self.pre_text + settings[1] + ' ' + amount + ' ' + __self.config.currency + ' ' + settings[2] + ' ' + settings[3]});
  959. });
  960. }
  961. } else {
  962. __self.irc.emit('message', {message:__self.pre_text + 'User was not found, use the push command to add a new user'});
  963. }
  964. });
  965. };
  966. /**
  967. * ============================================
  968. * AUCTION SYSTEM
  969. * --------------------------------------------
  970. */
  971. Currency.prototype.auction = function (status) {
  972. var __self = this;
  973. switch (status) {
  974. case true:
  975. if (!__self.votes_status) {
  976. if (!__self.bets_status) {
  977. if (!__self.raffle_status) {
  978. if (!__self.auction_status) {
  979. // open up the auction
  980. __self.auction_status = true;
  981. // request toggle
  982. if (__self.temp.raffle_toggle) {
  983. __self.temp.auction_toggle = __self.temp.raffle_toggle;
  984. } else {
  985. __self.temp.auction_toggle = __self.coin_toggle;
  986. }
  987. __self.coin_toggle = false;
  988. // default request timer
  989. if (__self.temp.raffle_timer && __self.temp.raffle_timer_reset) {
  990. __self.temp.auction_timer = __self.temp.raffle_timer;
  991. __self.temp.auction_timer_reset = __self.temp.raffle_timer_reset;
  992. } else {
  993. __self.temp.auction_timer = __self.coin_response_timer;
  994. __self.temp.auction_timer_reset = __self.coin_response_reset;
  995. }
  996. __self.coin_response_timer = 3000;
  997. __self.coin_response_reset = true;
  998. // clear previous bids
  999. __self.auction_bids = [];
  1000. __self.auction_previous_bid = {};
  1001. // auction open response
  1002. __self.irc.emit('message', {message:__self.pre_text + 'Auction opened, accepting bids'})
  1003. } else {
  1004. // auction is already open response
  1005. __self.irc.emit('message', {message:__self.pre_text + 'Auction already in progress'});
  1006. }
  1007. } else {
  1008. // raffle currently running
  1009. __self.irc.emit('message', {message:__self.pre_text + 'You must close the raffle before you can open an auction'});
  1010. }
  1011. } else {
  1012. // gambling currently running
  1013. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open an auction'});
  1014. }
  1015. }
  1016. break;
  1017. case false:
  1018. if (__self.auction_status) {
  1019. // close the auction
  1020. __self.auction_status = false;
  1021. // request toggle
  1022. __self.coin_toggle = __self.temp.auction_toggle;
  1023. delete __self.temp.auction_toggle;
  1024. // default request timer
  1025. __self.coin_response_timer = __self.temp.auction_timer;
  1026. __self.coin_response_reset = __self.temp.auction_timer_reset;
  1027. delete __self.temp.auction_timer;
  1028. delete __self.temp.auction_timer_reset;
  1029. // clear response timers
  1030. clearTimeout(__self.auction_bid_response);
  1031. clearInterval(__self.auction_bid_response_long);
  1032. if (__self.auction_bids.length > 0) {
  1033. // pick a winner response
  1034. for (var i = 0; i < __self.auction_bids.length; i++) {
  1035. if (__self.auction_bids[i].bid === utils.max(__self.auction_bids)) {
  1036. __self.irc.emit('message', {message:__self.pre_text + 'Auction closed, Winner: ' + __self.auction_bids[i].viewer + ' @ ' + __self.auction_bids[i].bid});
  1037. // save the winners info for draw refund
  1038. __self.auction_previous_bid.viewer = __self.auction_bids[i].viewer;
  1039. __self.auction_previous_bid.bid = __self.auction_bids[i].bid;
  1040. // remove winners money
  1041. var sql = '';
  1042. sql += 'UPDATE viewers ';
  1043. sql += 'SET points = points - ' + __self.auction_bids[i].bid + ' ';
  1044. sql += 'WHERE user = \'' + __self.auction_bids[i].viewer + '\'';
  1045. __self.db.execute(sql, function() {});
  1046. // remove winner from main list
  1047. __self.auction_bids.splice(i, 1);
  1048. break;
  1049. }
  1050. }
  1051. } else {
  1052. // no bidders to pick from response
  1053. __self.irc.emit('message', {message:__self.pre_text + 'Auction closed, no bidders to pick a winner'});
  1054. }
  1055. } else {
  1056. // auction is already open response
  1057. __self.irc.emit('message', {message:__self.pre_text + 'Auction is already closed'});
  1058. }
  1059. break;
  1060. case 'cancel':
  1061. if (__self.auction_status) {
  1062. // close the auction
  1063. __self.auction_status = false;
  1064. // request toggle
  1065. __self.coin_toggle = __self.temp.auction_toggle;
  1066. delete __self.temp.auction_toggle;
  1067. // default request timer
  1068. __self.coin_response_timer = __self.temp.auction_timer;
  1069. __self.coin_response_reset = __self.temp.auction_timer_reset;
  1070. delete __self.temp.auction_timer;
  1071. delete __self.temp.auction_timer_reset;
  1072. // clear response timers
  1073. clearTimeout(__self.auction_bid_response);
  1074. clearInterval(__self.auction_bid_response_long);
  1075. // clear previous bids
  1076. __self.auction_bids = [];
  1077. __self.auction_previous_bid = {};
  1078. // auction cancelled notification
  1079. __self.irc.emit('message', {message:__self.pre_text + 'Auction has been cancelled'});
  1080. } else {
  1081. // auction cancelled notification
  1082. __self.irc.emit('message', {message:__self.pre_text + 'Auction is not opened'});
  1083. }
  1084. break;
  1085. }
  1086. };
  1087. Currency.prototype.bid = function (caller, amount) {
  1088. var __self = this;
  1089. function find_duplicate(amount) {
  1090. var duplicate = false;
  1091. for (var i = 0; i < __self.auction_bids.length; i++) {
  1092. if (__self.auction_bids[i].bid === amount) {
  1093. duplicate = true;
  1094. break;
  1095. }
  1096. }
  1097. return duplicate;
  1098. }
  1099. if (__self.auction_status) {
  1100. // verify that bidder has the coins for bidding
  1101. __self.query_coins(caller, function (rows) {
  1102. var has_tickets = false;
  1103. // only add bid if they have the enough to pay
  1104. if (rows[0].points >= amount) {
  1105. if (__self.auction_bids.length > 0) {
  1106. // check if an existing bid exists and modify it
  1107. for (var i = 0; i < __self.auction_bids.length; i++) {
  1108. if (__self.auction_bids[i].viewer === caller) {
  1109. has_tickets = true;
  1110. // check if bid is higher then original and not a duplicate
  1111. if (__self.auction_bids[i].bid < amount && !find_duplicate(amount)) {
  1112. __self.auction_bids[i].bid = amount;
  1113. }
  1114. break;
  1115. }
  1116. }
  1117. // add new bids to list if they are not a duplicate
  1118. if (!has_tickets && !find_duplicate(amount)) {
  1119. __self.auction_bids.push({viewer: caller, bid: amount});
  1120. }
  1121. } else {
  1122. // push first bid
  1123. __self.auction_bids.push({viewer: caller, bid: amount});
  1124. }
  1125. }
  1126. // clear timers on flood
  1127. clearTimeout(__self.auction_bid_response);
  1128. clearInterval(__self.auction_bid_response_long);
  1129. // reply after set amount of bids
  1130. if ((__self.auction_bids.length % __self.max_requests) === 0) {
  1131. // bulk flood response
  1132. for (var i = 0; i < __self.auction_bids.length; i++) {
  1133. if (__self.auction_bids[i].bid === utils.max(__self.auction_bids)) {
  1134. __self.irc.msg(__self.pre_text + 'Highest bid, ' + __self.auction_bids[i].viewer + ' @ ' + __self.auction_bids[i].bid);
  1135. }
  1136. }
  1137. } else {
  1138. // response after time without flood has passed
  1139. var viewer, bid;
  1140. for (var i = 0; i < __self.auction_bids.length; i++) {
  1141. if (__self.auction_bids[i].bid === utils.max(__self.auction_bids)) {
  1142. viewer = __self.auction_bids[i].viewer;
  1143. bid = __self.auction_bids[i].bid;
  1144. }
  1145. }
  1146. if (viewer !== undefined && bid !== undefined && __self.auction_status) {
  1147. var msg = __self.pre_text + 'Highest bid, ' + viewer + ' @ ' + bid;
  1148. __self.auction_bid_response = setTimeout(function () {__self.irc.emit('message', {message:msg, timer: 1});}, 5000);
  1149. __self.auction_bid_response_long = setInterval(function () {__self.irc.emit('message', {message:msg, timer: 1});}, 30000);
  1150. }
  1151. }
  1152. });
  1153. }
  1154. };
  1155. Currency.prototype.next_auction_winner = function () {
  1156. var __self = this, empty_list = [];
  1157. // custom dialog when the bidder list is empty
  1158. empty_list.push('Hey, I just met you and this is crazy, but there\'s no more bidders, so start an new auction maybe?');
  1159. empty_list.push('Are there more bidders? Well, to tell you the truth, in all this excitement I kind of lost track myself.');
  1160. empty_list.push('Heyyyyyy there\'s no more bidders, Op, op, op, op, Open Auction Style.');
  1161. empty_list.push('Da bids! Da bids! Where are all da bids, boss?');
  1162. if (!__self.auction_status) {
  1163. // get next highest bidder or prompt to open new auction
  1164. if (__self.auction_bids.length > 0) {
  1165. for (var i = 0; i < __self.auction_bids.length; i++) {
  1166. if (__self.auction_bids[i].bid === utils.max(__self.auction_bids)) {
  1167. __self.irc.emit('message',{message:__self.pre_text + 'Drawing the next highest bid: ' + __self.auction_bids[i].viewer + ' @ ' + __self.auction_bids[i].bid});
  1168. // refund previous winner's money
  1169. var sql = '';
  1170. sql += 'UPDATE viewers ';
  1171. sql += 'SET points = points + ' + __self.auction_previous_bid.bid + ' ';
  1172. sql += 'WHERE user = \'' + __self.auction_previous_bid.viewer + '\'';
  1173. __self.db.execute(sql, function() {});
  1174. // save the new winner's info for next draw
  1175. __self.auction_previous_bid.viewer = __self.auction_bids[i].viewer;
  1176. __self.auction_previous_bid.bid = __self.auction_bids[i].bid;
  1177. // remove winners money
  1178. sql = '';
  1179. sql += 'UPDATE viewers ';
  1180. sql += 'SET points = points - ' + __self.auction_bids[i].bid + ' ';
  1181. sql += 'WHERE user = \'' + __self.auction_bids[i].viewer + '\'';
  1182. __self.db.execute(sql, function() {});
  1183. // remove winner from main list
  1184. __self.auction_bids.splice(i, 1);
  1185. break;
  1186. }
  1187. }
  1188. } else {
  1189. // check if a previous viewer is saved
  1190. if (__self.auction_previous_bid.viewer !== null) {
  1191. // refund previous winner's money
  1192. var sql = '';
  1193. sql += 'UPDATE viewers ';
  1194. sql += 'SET points = points + ' + __self.auction_previous_bid.bid + ' ';
  1195. sql += 'WHERE user = \'' + __self.auction_previous_bid.viewer + '\'';
  1196. __self.db.execute(sql, function() {});
  1197. // clear previous bid
  1198. __self.auction_previous_bid = {};
  1199. }
  1200. // notify that there's no more bids
  1201. __self.irc.emit('message',{message:__self.pre_text + utils.selectRandomArrayItem(empty_list)});
  1202. }
  1203. }
  1204. };
  1205. /**
  1206. * ============================================
  1207. * RAFFLE SYSTEM
  1208. * --------------------------------------------
  1209. */
  1210. Currency.prototype.raffle = function (status) {
  1211. var __self = this;
  1212. switch (status) {
  1213. case true:
  1214. if (!__self.votes_status) {
  1215. if (!__self.bets_status) {
  1216. if (!__self.auction_status) {
  1217. if (!__self.raffle_status) {
  1218. // open up a raffle
  1219. __self.raffle_status = true;
  1220. // request toggle
  1221. if (__self.temp.auction_toggle) {
  1222. __self.temp.raffle_toggle = __self.temp.auction_toggle;
  1223. } else {
  1224. __self.temp.raffle_toggle = __self.coin_toggle;
  1225. }
  1226. __self.coin_toggle = false;
  1227. // default request timer
  1228. if (__self.temp.auction_timer && __self.temp.auction_timer_reset) {
  1229. __self.temp.raffle_timer = __self.temp.auction_timer;
  1230. __self.temp.raffle_timer_reset = __self.temp.auction_timer_reset;
  1231. } else {
  1232. __self.temp.raffle_timer = __self.coin_response_timer;
  1233. __self.temp.raffle_timer_reset = __self.coin_response_reset;
  1234. }
  1235. __self.coin_response_timer = 3000;
  1236. __self.coin_response_reset = true;
  1237. // save previous raffle settings in case
  1238. // a new one is opened on accident
  1239. __self.raffle_restore_ticket_requests = __self.raffle_ticket_requests;
  1240. __self.raffle_restore_tickets = __self.raffle_tickets;
  1241. // clear previous tickets
  1242. __self.raffle_ticket_requests = [];
  1243. __self.raffle_tickets = [];
  1244. // raffle open response
  1245. __self.irc.emit('message',{message:__self.pre_text + 'Raffle opened'});
  1246. __self.irc.emit('message',{message:'+ Tickets cost ' + __self.raffle_ticket_cost + ' ' + __self.config.currency.toLowerCase() + ' / Maximum of ' + __self.raffle_max_tickets + ' tickets per viewer'});
  1247. } else {
  1248. // raffle in progress response
  1249. __self.irc.emit('message',{message:__self.pre_text + 'Raffle already in progress'});
  1250. }
  1251. } else {
  1252. // auction in progress
  1253. __self.irc.emit('message', {message:__self.pre_text + 'You must close the auction before you can open an a raffle'});
  1254. }
  1255. } else {
  1256. // gambling currently running
  1257. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open a raffle'});
  1258. }
  1259. }
  1260. break;
  1261. case false:
  1262. if (__self.raffle_status) {
  1263. // close the raffle
  1264. __self.raffle_status = false;
  1265. // request toggle
  1266. __self.coin_toggle = __self.temp.raffle_toggle;
  1267. delete __self.temp.raffle_toggle;
  1268. // default request timer
  1269. __self.coin_response_timer = __self.temp.raffle_timer;
  1270. __self.coin_response_reset = __self.temp.raffle_timer_reset;
  1271. delete __self.temp.raffle_timer;
  1272. delete __self.temp.raffle_timer_reset;
  1273. // validation / winner / deduction
  1274. __self.raffle_winner();
  1275. } else {
  1276. // raffle is already open response
  1277. __self.irc.emit('message',{message:__self.pre_text + 'Raffle is already closed'});
  1278. }
  1279. break;
  1280. case 'cancel':
  1281. if (__self.raffle_status) {
  1282. // close the raffle
  1283. __self.raffle_status = false;
  1284. // request toggle
  1285. __self.coin_toggle = __self.temp.raffle_toggle;
  1286. delete __self.temp.raffle_toggle;
  1287. // default request timer
  1288. __self.coin_response_timer = __self.temp.raffle_timer;
  1289. __self.coin_response_reset = __self.temp.raffle_timer_reset;
  1290. delete __self.temp.raffle_timer;
  1291. delete __self.temp.raffle_timer_reset;
  1292. // clear previous tickets
  1293. __self.raffle_ticket_requests = [];
  1294. __self.raffle_tickets = [];
  1295. // raffle cancelled notification
  1296. __self.irc.emit('message', {message:__self.pre_text + 'Raffle has been cancelled'});
  1297. } else {
  1298. // raffle cancelled notification
  1299. __self.irc.emit('message', {message:__self.pre_text + 'Raffle is not opened'});
  1300. }
  1301. break;
  1302. case 'restore':
  1303. if (__self.raffle_status) {
  1304. // close raffle
  1305. __self.raffle_status = false;
  1306. // restore previous raffle tickets
  1307. __self.raffle_ticket_requests = __self.raffle_restore_ticket_requests;
  1308. __self.raffle_tickets = __self.raffle_restore_tickets;
  1309. __self.irc.emit('message', {message:__self.pre_text + 'Previous raffle has been restored'});
  1310. } else {
  1311. // raffle restore failed notification
  1312. __self.irc.emit('message', {message:__self.pre_text + 'Raffle is closed, unable to restore'});
  1313. }
  1314. break;
  1315. }
  1316. };
  1317. Currency.prototype.collect_tickets = function (caller, amount) {
  1318. var __self = this, has_tickets = false;
  1319. if (__self.raffle_ticket_requests.length > 0) {
  1320. // check if viewer already has tickets
  1321. for (var i = 0; i < __self.raffle_ticket_requests.length; i++) {
  1322. if (__self.raffle_ticket_requests[i].viewer === caller) {
  1323. has_tickets = true;
  1324. if (amount <= __self.raffle_max_tickets && amount >= 1) {
  1325. __self.raffle_ticket_requests[i].tickets = amount;
  1326. } else if (amount === 0) {
  1327. __self.raffle_ticket_requests.splice(i, 1);
  1328. }
  1329. break;
  1330. }
  1331. }
  1332. // if viewer doesn't have tickets and meets > 1 < max req add their request
  1333. if (!has_tickets && amount <= __self.raffle_max_tickets && amount >= 1 && amount !== 0) {
  1334. __self.raffle_ticket_requests.push({viewer: caller, tickets: amount});
  1335. }
  1336. } else {
  1337. // push first ticket if > 1 < max
  1338. if (amount <= __self.raffle_max_tickets && amount >= 1 && amount !== 0) {
  1339. __self.raffle_ticket_requests.push({viewer: caller, tickets: amount});
  1340. }
  1341. }
  1342. };
  1343. Currency.prototype.raffle_winner = function () {
  1344. var __self = this, sql = '';
  1345. if (__self.raffle_ticket_requests.length > 0) {
  1346. // setup sql to grab all viewers that request coins from the database
  1347. sql += 'SELECT * FROM viewers WHERE ';
  1348. for (var i = 0; i < __self.raffle_ticket_requests.length; i++) {
  1349. if (i !== __self.raffle_ticket_requests.length - 1) {
  1350. sql += 'user=\'' + __self.raffle_ticket_requests[i].viewer.toLowerCase() + '\' OR ';
  1351. } else {
  1352. sql += 'user=\'' + __self.raffle_ticket_requests[i].viewer.toLowerCase() + '\'';
  1353. }
  1354. }
  1355. // execute viewer search query
  1356. __self.db.execute(sql, function(rows) {
  1357. // currency validation
  1358. // - this takes the results of the query and uses the names from the database
  1359. // to filter through the viewers that requested tickets (since they have to be in the
  1360. // database in the first place)
  1361. // - during the filtering process the viewers requested tickets are multiplied by the
  1362. // ticket cost and compared against their currency amount
  1363. // - if the viewer has the funds, their tickets are added and the sql is updated to include their
  1364. // deduction
  1365. sql = '';
  1366. for (var i = 0; i < rows.length; i++) {
  1367. for (var j = 0; j < __self.raffle_ticket_requests.length; j++) {
  1368. if (__self.raffle_ticket_requests[j].viewer.toLowerCase() === rows[i].user) {
  1369. var money = __self.raffle_ticket_requests[j].tickets * __self.raffle_ticket_cost;
  1370. if (rows[i].points >= money) {
  1371. for (var k = 1; k <= __self.raffle_ticket_requests[j].tickets; k++) {
  1372. __self.raffle_tickets.push(__self.raffle_ticket_requests[j].viewer);
  1373. }
  1374. if (i !== rows.length - 1) {
  1375. sql += 'UPDATE viewers ';
  1376. sql += 'SET points = points - ' + money + ' ';
  1377. sql += 'WHERE user = \'' + rows[i].user + '\'; ';
  1378. } else {
  1379. sql += 'UPDATE viewers ';
  1380. sql += 'SET points = points - ' + money + ' ';
  1381. sql += 'WHERE user = \'' + rows[i].user + '\'';
  1382. }
  1383. }
  1384. break;
  1385. }
  1386. }
  1387. }
  1388. // randomize array before selecting a random winner
  1389. __self.raffle_tickets.sort(function () {return 0.5 - Math.random();});
  1390. // select random ticket from array
  1391. var winner = utils.selectRandomArrayItem(__self.raffle_tickets);
  1392. // count winner's tickets
  1393. var winning_ticket_amount;
  1394. for (var i = 0; i < __self.raffle_ticket_requests.length; i++) {
  1395. if (__self.raffle_ticket_requests[i].viewer === winner) {
  1396. winning_ticket_amount = __self.raffle_ticket_requests[i].tickets;
  1397. break;
  1398. }
  1399. }
  1400. // output winner to chat
  1401. __self.irc.emit('message', {message:__self.pre_text + 'Raffle closed, ' + __self.raffle_tickets.length + ' tickets purchased!'});
  1402. __self.irc.emit('message', {message:'+ Winner: ' + winner + ' (' + winning_ticket_amount + ' tickets purchased)'});
  1403. // remove one ticket from raffle bowl
  1404. if (__self.raffle_tickets.indexOf(winner) >= 0 ) {
  1405. __self.raffle_tickets.splice(__self.raffle_tickets.indexOf(winner), 1);
  1406. }
  1407. // execute query
  1408. __self.db.execute(sql, function () {});
  1409. });
  1410. } else {
  1411. // no tickets to pick from response
  1412. __self.irc.emit('message', {message:__self.pre_text + 'Raffle closed, no tickets to draw a winner'});
  1413. }
  1414. };
  1415. Currency.prototype.next_raffle_winner = function () {
  1416. var __self = this, empty_list = [];
  1417. // custom dialog when there are no more raffle tickets
  1418. empty_list.push('Hey, I just met you and this is crazy, but there\'s no more tickets, so start an new raffle maybe?');
  1419. empty_list.push('Are there more tickets? Well, to tell you the truth, in all this excitement I kind of lost track myself.');
  1420. empty_list.push('Heyyyyyy there\'s no more tickets, Op, op, op, op, Open Raffle Style.');
  1421. empty_list.push('Da tickets! Da tickets! Where are all da tickets, boss?');
  1422. if (!__self.raffle_status) {
  1423. // draw next ticket or prompt to open new raffle
  1424. if (__self.raffle_tickets.length > 0) {
  1425. // randomize array before selecting a random winner
  1426. __self.raffle_tickets.sort(function () {return 0.5 - Math.random();});
  1427. // select random ticket from array
  1428. var winner = utils.selectRandomArrayItem(__self.raffle_tickets);
  1429. // count next winner's tickets
  1430. var winning_ticket_amount;
  1431. for (var i = 0; i < __self.raffle_ticket_requests.length; i++) {
  1432. if (__self.raffle_ticket_requests[i].viewer === winner) {
  1433. winning_ticket_amount = __self.raffle_ticket_requests[i].tickets;
  1434. break;
  1435. }
  1436. }
  1437. // output winner to chat
  1438. __self.irc.emit('message', {message:__self.pre_text + 'Drawing next ticket'});
  1439. __self.irc.emit('message', {message:'+ Winner: ' + winner + ' (' + winning_ticket_amount + ' tickets purchased)'});
  1440. // remove one ticket from raffle bowl
  1441. if (__self.raffle_tickets.indexOf(winner) >= 0 ) {
  1442. __self.raffle_tickets.splice(__self.raffle_tickets.indexOf(winner), 1);
  1443. }
  1444. } else {
  1445. __self.irc.emit('message', {message:__self.pre_text + utils.selectRandomArrayItem(empty_list)});
  1446. }
  1447. }
  1448. };
  1449. /*
  1450. * VOTING SYSTEM
  1451. * ------------
  1452. */
  1453. Currency.prototype.votes = function(status, data) {
  1454. var __self = this;
  1455. switch(status){
  1456. case true:
  1457. if (!__self.bets_status) {
  1458. if (!__self.auction_status) {
  1459. if (!__self.raffle_status) {
  1460. if (!__self.votes_status && !__self.votes_payout) {
  1461. var wager_msg = '';
  1462. __self.votes_status = true;
  1463. __self.votes_payout = true;
  1464. __self.votes_board = [];
  1465. __self.votes_viewers = [];
  1466. __self.votes_total = 0;
  1467. __self.votes_board = data.join().split(',').filter(function(n){return n}).slice(6).map(function(n){return {name: n, num: 0, total: 0};});
  1468. for (var i = 0; i < __self.votes_board.length; i++) {
  1469. __self.votes_board[i].idx = i;
  1470. }
  1471. for (var i = 0; i < __self.votes_board.length; i++) {
  1472. if (i !== __self.votes_board.length - 1) {
  1473. wager_msg += '"!' + __self.votes_board[i].name + '" / ';
  1474. } else {
  1475. wager_msg += '"!' + __self.votes_board[i].name + '"';
  1476. }
  1477. }
  1478. // output to chat
  1479. __self.irc.emit('message', {message:__self.pre_text + 'Voting is now open'});
  1480. __self.irc.emit('message', {message:'+ Type ' + wager_msg + ' and the vote amount to enter'});
  1481. } else {
  1482. if (__self.votes_payout) {
  1483. // payout pending message
  1484. __self.irc.emit('message', {message:__self.pre_text + 'Unable to take new votes until the last has been decided'});
  1485. } else {
  1486. __self.irc.emit('message', {message:__self.pre_text + 'Voting already in progress'});
  1487. }
  1488. }
  1489. // FIXME: messages below should be "cant open a vote until X is over/closed" instead? test it
  1490. } else {
  1491. // raffle in progress
  1492. __self.irc.emit('message', {message:__self.pre_text + 'Voting must be closed before you can open a raffle'});
  1493. }
  1494. } else {
  1495. // auction currently running
  1496. __self.irc.emit('message', {message:__self.pre_text + 'Voting must be closed before you can open an auction'});
  1497. }
  1498. }
  1499. break;
  1500. case false:
  1501. if (__self.votes_status && __self.votes_payout) {
  1502. __self.votes_status = false;
  1503. __self.votes_deduct_votes();
  1504. // output to chat
  1505. if (__self.votes_viewers.length > 0) {
  1506. __self.irc.emit('message', {message:__self.pre_text + 'Voting is now closed'});
  1507. for (var i = 0; i < __self.votes_board.length; i++) {
  1508. __self.irc.emit('message', {message:'+ ' + __self.votes_board[i].name + ' with ' + __self.votes_board[i].num + ' supporters totaling ' + __self.votes_board[i].total + ' votes'});
  1509. }
  1510. //
  1511. } else {
  1512. __self.irc.emit('message', {message:__self.pre_text + 'Voting closed, no votes were cast.'});
  1513. }
  1514. }
  1515. break;
  1516. case 'pool':
  1517. function do_work() {
  1518. if (__self.votes_board.length > 0) {
  1519. __self.irc.emit('message', {message:__self.pre_text + 'Current voting pool is:'});
  1520. for (var i = 0; i < __self.votes_board.length; i++) {
  1521. __self.irc.emit('message', {message:'+ ' + __self.votes_board[i].name + ' with ' + __self.votes_board[i].num + ' supporters totaling ' + __self.votes_board[i].total + ' votes'});
  1522. }
  1523. } else {
  1524. __self.irc.emit('message', {message:__self.pre_text + 'No current vote.'});
  1525. }
  1526. }
  1527. if (__self.votes_response_reset) {
  1528. __self.votes_response = setTimeout(function () {do_work();}, __self.votes_response_timer);
  1529. } else {
  1530. setTimeout(function () {do_work();}, __self.votes_response_timer);
  1531. }
  1532. break;
  1533. case 'winner':
  1534. if (!__self.votes_status) {
  1535. if (__self.votes_payout) {
  1536. for (var i = 0; i < __self.votes_board.length; i++) {
  1537. if (data == __self.votes_board[i].name) {
  1538. __self.votes_award_winner(__self.votes_board[i]);
  1539. __self.irc.emit('message', {message:__self.pre_text + 'Winner:' + data + 'had the most votes!'});
  1540. }
  1541. }
  1542. } else {
  1543. __self.irc.emit('message', {message:__self.pre_text + 'A winner has already been decided.'});
  1544. }
  1545. } else {
  1546. __self.irc.emit('message', {message:__self.pre_text + 'Voting must be closed before you can have a winner'});
  1547. }
  1548. break;
  1549. case 'cancel':
  1550. if (!__self.votes_status) {
  1551. // Return all the currency back to original owners
  1552. if (__self.votes_payout) {
  1553. sql = '';
  1554. for (var i = 0; i < __self.votes_viewers.length; i++) {
  1555. if (__self.votes_viewers[i].amount > 0) {
  1556. if (sql.length > 0) {
  1557. sql += '; ';
  1558. }
  1559. sql += 'UPDATE viewers ';
  1560. sql += 'SET points = points + ' + __self.votes_viewers[i].amount + ' ';
  1561. sql += 'WHERE user = \'' + __self.votes_viewers[i].viewer + '\'';
  1562. }
  1563. }
  1564. __self.db.execute(sql, function() {
  1565. __self.irc.emit('message', {message:__self.pre_text + 'Vote was canceled, all ' + __self.config.currency.toLowerCase() + ' returned.'});
  1566. });
  1567. __self.votes_payout = false;
  1568. } else {
  1569. __self.irc.emit('message', {message:__self.pre_text + 'No vote to cancel.'});
  1570. }
  1571. } else {
  1572. __self.votes_status = false;
  1573. __self.votes_payout = false;
  1574. __self.irc.emit('message', {message:__self.pre_text + 'Current vote was canceled.'});
  1575. }
  1576. break;
  1577. }
  1578. };
  1579. Currency.prototype.collect_votes = function (caller, vote, amount) {
  1580. var __self = this, has_vote = false;
  1581. function do_work() {
  1582. var multi_response = [];
  1583. for (var i = 0; i < __self.votes_board.length; i++) {
  1584. multi_response.push([]);
  1585. }
  1586. if (__self.votes_flood.length > 0) {// send flood requests
  1587. var vote;
  1588. // setup vote response
  1589. for (var i = 0; i < __self.votes_flood.length; i++) {
  1590. vote = __self.votes_viewers[__self.votes_flood[i]];
  1591. multi_response[vote.vote].push(vote.viewer + ' (' + vote.amount + ')');
  1592. }
  1593. for (var i = 0; i < __self.votes_board.length; i++) {
  1594. if (multi_response[i].length > 0) {
  1595. __self.irc.emit('message', {message:'New votes for ' + __self.votes_board[i].name + ': ' + multi_response[i].join(', '), timer: 1});
  1596. }
  1597. }
  1598. }
  1599. // clear flood requests
  1600. __self.votes_flood = [];
  1601. }
  1602. // Bound amount by positive currency
  1603. __self.query_coins(caller, function (rows) {
  1604. for (var i = 0; i < rows.length; i++) {
  1605. amount = Math.min(amount, rows[i].points);
  1606. }
  1607. if (__self.votes_viewers.length > 0) {
  1608. for (var i = 0; i < __self.votes_viewers.length; i++) {
  1609. if (__self.votes_viewers[i].viewer === caller) {
  1610. has_vote = true;
  1611. if (amount > __self.votes_viewers[i].amount) {
  1612. __self.votes_board[__self.votes_viewers[i].vote].num -= 1;
  1613. __self.votes_board[__self.votes_viewers[i].vote].total -= __self.votes_viewers[i].amount;
  1614. __self.votes_viewers[i].vote = vote.idx;
  1615. __self.votes_viewers[i].amount = amount;
  1616. __self.votes_board[vote.idx].num += 1;
  1617. __self.votes_board[vote.idx].total += amount;
  1618. // add flood users to array
  1619. if (__self.votes_flood.indexOf(i) < 0) {
  1620. __self.votes_flood.push(i);
  1621. }
  1622. }
  1623. break;
  1624. }
  1625. }
  1626. if (!has_vote && amount >= 1 && amount !== 0) {
  1627. __self.votes_viewers.push({viewer: caller, vote: vote.idx, amount: amount});
  1628. __self.votes_board[vote.idx].num += 1;
  1629. __self.votes_board[vote.idx].total += amount;
  1630. __self.votes_flood.push(__self.votes_viewers.length - 1);
  1631. }
  1632. } else {
  1633. if (amount >= 1 && amount !== 0) {
  1634. __self.votes_viewers.push({viewer: caller, vote: vote.idx, amount: amount});
  1635. __self.votes_board[vote.idx].num += 1;
  1636. __self.votes_board[vote.idx].total += amount;
  1637. __self.votes_flood.push(__self.votes_viewers.length - 1);
  1638. }
  1639. }
  1640. // check if flood has a set amount of requests and output
  1641. // if not, set the output timer
  1642. if (__self.votes_flood.length === __self.max_requests) {
  1643. do_work();
  1644. } else {
  1645. if (__self.votes_response_reset) {
  1646. __self.votes_response = setTimeout(function () {do_work();}, __self.votes_response_timer);
  1647. } else {
  1648. setTimeout(function () {do_work();}, __self.votes_response_timer);
  1649. }
  1650. }
  1651. });
  1652. };
  1653. Currency.prototype.votes_deduct_votes = function () {
  1654. var __self = this;
  1655. if (__self.votes_viewers.length > 0) {
  1656. // Don't trust the on-the-fly numbers
  1657. for (var i = 0; i < __self.votes_board.length; i++) {
  1658. __self.votes_board[i].num = 0;
  1659. __self.votes_board[i].total = 0;
  1660. }
  1661. __self.votes_total = 0;
  1662. // Remove the points and add them to an escrow
  1663. sql = '';
  1664. for (var i = 0; i < __self.votes_viewers.length; i++) {
  1665. if (__self.votes_viewers[i].amount > 0) {
  1666. if (sql.length > 0) {
  1667. sql += '; ';
  1668. }
  1669. sql += 'UPDATE viewers ';
  1670. sql += 'SET points = points - ' + __self.votes_viewers[i].amount + ' ';
  1671. sql += 'WHERE user = \'' + __self.votes_viewers[i].viewer + '\'';
  1672. __self.votes_board[__self.votes_viewers[i].vote].num += 1;
  1673. __self.votes_board[__self.votes_viewers[i].vote].total += __self.votes_viewers[i].amount;
  1674. __self.votes_total += __self.votes_viewers[i].amount;
  1675. }
  1676. }
  1677. __self.db.execute(sql, function() {});
  1678. }
  1679. };
  1680. Currency.prototype.votes_award_winner = function (winner) {
  1681. var __self = this;
  1682. if (__self.votes_payout) {
  1683. // set payout to complete
  1684. __self.votes_payout = false;
  1685. }
  1686. // Clear the board
  1687. __self.votes_board = [];
  1688. __self.votes_viewers = [];
  1689. __self.votes_total = 0;
  1690. };
  1691. /**
  1692. * ============================================
  1693. * BETTING SYSTEM
  1694. * --------------------------------------------
  1695. */
  1696. Currency.prototype.bets = function(status, data) {
  1697. var __self = this;
  1698. switch(status){
  1699. case true:
  1700. if (!__self.auction_status) {
  1701. if (!__self.raffle_status) {
  1702. if (!__self.bets_status && !__self.bets_payout) {
  1703. var wager_msg = '';
  1704. // open up bets
  1705. __self.bets_status = true;
  1706. __self.bets_payout = true;
  1707. // clear previous board / bets
  1708. __self.bets_board = [];
  1709. __self.bets_viewers = [];
  1710. __self.bets_total = 0;
  1711. // create new betting board
  1712. __self.bets_board = data.join().split(',').filter(function(n){return n}).slice(6).map(function(n){return {name: n, num: 0, total: 0};});
  1713. for (var i = 0; i < __self.bets_board.length; i++) {
  1714. __self.bets_board[i].idx = i;
  1715. }
  1716. // create chat message on how to place a bet
  1717. for (var i = 0; i < __self.bets_board.length; i++) {
  1718. if (i !== __self.bets_board.length - 1) {
  1719. wager_msg += '"!' + __self.bets_board[i].name + '" / ';
  1720. } else {
  1721. wager_msg += '"!' + __self.bets_board[i].name + '"';
  1722. }
  1723. }
  1724. // output to chat
  1725. __self.irc.emit('message', {message:__self.pre_text + 'Betting is now open'});
  1726. __self.irc.emit('message', {message:'+ Type ' + wager_msg + ' and the bet amount to enter'});
  1727. } else {
  1728. if (__self.bets_payout) {
  1729. // payout pending message
  1730. __self.irc.emit('message', {message:__self.pre_text + 'Unable to take new bets until previous have been paid out'});
  1731. } else {
  1732. // gambling is already open response
  1733. __self.irc.emit('message', {message:__self.pre_text + 'Betting already in progress'});
  1734. }
  1735. }
  1736. // FIXME: messages below should be "cant open a bet until X is over/closed" instead? test it
  1737. } else {
  1738. // raffle in progress
  1739. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open a raffle'});
  1740. }
  1741. } else {
  1742. // auction currently running
  1743. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open an auction'});
  1744. }
  1745. break;
  1746. case false:
  1747. if (__self.bets_status && __self.bets_payout) {
  1748. // close out bets
  1749. __self.bets_status = false;
  1750. // deduct bets from viewers amounts
  1751. __self.bets_deduct_bets();
  1752. // output to chat
  1753. if (__self.bets_viewers.length > 0) {
  1754. __self.irc.emit('message', {message:__self.pre_text + 'Betting is now closed'});
  1755. for (var i = 0; i < __self.bets_board.length; i++) {
  1756. if(parseInt(__self.bets_board[i].total,10) > parseInt(__self.score_bet_pool, 10)) {
  1757. __self.score_bet_pool = __self.bets_board[i].total;
  1758. sql = 'INSERT INTO highscores (`value`, `name`) VALUES (\'' + __self.bets_board[i].total + '\', \'bet_pool\') ON DUPLICATE KEY UPDATE value=values(value);';
  1759. __self.db.execute(sql, function() {});
  1760. __self.irc.emit('message', {message:'+ ' + __self.bets_board[i].name + ' with ' + __self.bets_board[i].num + ' bets totaling ' + __self.bets_board[i].total + '. New record set!!'});
  1761. } else {
  1762. __self.irc.emit('message', {message:'+ ' + __self.bets_board[i].name + ' with ' + __self.bets_board[i].num + ' bets totaling ' + __self.bets_board[i].total});
  1763. }
  1764. }
  1765. //
  1766. } else {
  1767. __self.irc.emit('message', {message:__self.pre_text + 'Betting closed, no bets were placed'});
  1768. }
  1769. }
  1770. break;
  1771. case 'pool':
  1772. function do_work() {
  1773. if (__self.bets_board.length > 0) {
  1774. __self.irc.emit('message', {message:__self.pre_text + 'Current betting pool is:'});
  1775. for (var i = 0; i < __self.bets_board.length; i++) {
  1776. __self.irc.emit('message', {message:'+ ' + __self.bets_board[i].name + ' with ' + __self.bets_board[i].num + ' bets totaling ' + __self.bets_board[i].total});
  1777. }
  1778. } else {
  1779. __self.irc.emit('message', {message:__self.pre_text + 'No current bet.'});
  1780. }
  1781. }
  1782. if (__self.bets_response_reset) {
  1783. __self.bets_response = setTimeout(function () {do_work();}, __self.bets_response_timer);
  1784. } else {
  1785. setTimeout(function () {do_work();}, __self.bets_response_timer);
  1786. }
  1787. break;
  1788. case 'winner':
  1789. if (!__self.bets_status) {
  1790. if (__self.bets_payout) {
  1791. for (var i = 0; i < __self.bets_board.length; i++) {
  1792. if (data == __self.bets_board[i].name) {
  1793. __self.bets_award_winner(__self.bets_board[i]);
  1794. __self.irc.emit('message', {message:__self.pre_text + 'Betting payed out to ' + data});
  1795. }
  1796. }
  1797. } else {
  1798. __self.irc.emit('message', {message:__self.pre_text + 'Betting has already payed out'});
  1799. }
  1800. } else {
  1801. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can have a winner'});
  1802. }
  1803. break;
  1804. case 'cancel':
  1805. if (!__self.bets_status) {
  1806. // Return all the currency back to original owners
  1807. if (__self.bets_payout) {
  1808. sql = '';
  1809. for (var i = 0; i < __self.bets_viewers.length; i++) {
  1810. if (__self.bets_viewers[i].amount > 0) {
  1811. if (sql.length > 0) {
  1812. sql += '; ';
  1813. }
  1814. sql += 'UPDATE viewers ';
  1815. sql += 'SET points = points + ' + __self.bets_viewers[i].amount + ' ';
  1816. sql += 'WHERE user = \'' + __self.bets_viewers[i].viewer + '\'';
  1817. }
  1818. }
  1819. __self.db.execute(sql, function() {
  1820. __self.irc.emit('message', {message:__self.pre_text + 'Bet was canceled, all ' + __self.config.currency.toLowerCase() + ' returned.'});
  1821. });
  1822. __self.bets_payout = false;
  1823. // Or just cancel the bets
  1824. } else {
  1825. __self.irc.emit('message', {message:__self.pre_text + 'No bet to cancel.'});
  1826. }
  1827. } else {
  1828. __self.bets_status = false;
  1829. __self.bets_payout = false;
  1830. __self.irc.emit('message', {message:__self.pre_text + 'Current bet was canceled.'});
  1831. }
  1832. break;
  1833. }
  1834. };
  1835. Currency.prototype.collect_bets = function (caller, bet, amount) {
  1836. var __self = this, has_bet = false;
  1837. function do_work() {
  1838. var multi_response = [];
  1839. for (var i = 0; i < __self.bets_board.length; i++) {
  1840. multi_response.push([]);
  1841. }
  1842. if (__self.bets_flood.length > 0) {// send flood requests
  1843. var bet;
  1844. // setup bet response
  1845. for (var i = 0; i < __self.bets_flood.length; i++) {
  1846. bet = __self.bets_viewers[__self.bets_flood[i]];
  1847. multi_response[bet.bet].push(bet.viewer + ' (' + bet.amount + ')');
  1848. // check for hiscore and set db appropriately
  1849. if ( parseInt(bet.amount, 10) > parseInt(__self.score_bet_single, 10) ) {
  1850. sql = 'INSERT INTO highscores (`value`, `user`, `name`) VALUES (\'' + amount + '\', \'' + caller + '\', \'bet_single\') ON DUPLICATE KEY UPDATE user=values(user), value=values(value);';
  1851. __self.db.execute(sql, function() {
  1852. __self.score_bet_single = amount;
  1853. __self.irc.emit('message', {message:__self.pre_text + ' ' + caller + ' set new record bet at ' + amount + '!'});
  1854. });
  1855. }
  1856. }
  1857. for (var i = 0; i < __self.bets_board.length; i++) {
  1858. if (multi_response[i].length > 0) {
  1859. __self.irc.emit('message', {message:'New bets for ' + __self.bets_board[i].name + ': ' + multi_response[i].join(', '), timer: 1});
  1860. }
  1861. }
  1862. }
  1863. // clear flood requests
  1864. __self.bets_flood = [];
  1865. }
  1866. // Bound amount by positive currency
  1867. __self.query_coins(caller, function (rows) {
  1868. for (var i = 0; i < rows.length; i++) {
  1869. amount = Math.min(amount, rows[i].points);
  1870. }
  1871. if (__self.bets_viewers.length > 0) {
  1872. for (var i = 0; i < __self.bets_viewers.length; i++) {
  1873. if (__self.bets_viewers[i].viewer === caller) {
  1874. has_bet = true;
  1875. if (amount > __self.bets_viewers[i].amount) {
  1876. __self.bets_board[__self.bets_viewers[i].bet].num -= 1;
  1877. __self.bets_board[__self.bets_viewers[i].bet].total -= __self.bets_viewers[i].amount;
  1878. __self.bets_viewers[i].bet = bet.idx;
  1879. __self.bets_viewers[i].amount = amount;
  1880. __self.bets_board[bet.idx].num += 1;
  1881. __self.bets_board[bet.idx].total += amount;
  1882. // add flood users to array
  1883. if (__self.bets_flood.indexOf(i) < 0) {
  1884. __self.bets_flood.push(i);
  1885. }
  1886. }
  1887. break;
  1888. }
  1889. }
  1890. if (!has_bet && amount >= 1 && amount !== 0) {
  1891. __self.bets_viewers.push({viewer: caller, bet: bet.idx, amount: amount});
  1892. __self.bets_board[bet.idx].num += 1;
  1893. __self.bets_board[bet.idx].total += amount;
  1894. __self.bets_flood.push(__self.bets_viewers.length - 1);
  1895. }
  1896. } else {
  1897. if (amount >= 1 && amount !== 0) {
  1898. __self.bets_viewers.push({viewer: caller, bet: bet.idx, amount: amount});
  1899. __self.bets_board[bet.idx].num += 1;
  1900. __self.bets_board[bet.idx].total += amount;
  1901. __self.bets_flood.push(__self.bets_viewers.length - 1);
  1902. }
  1903. }
  1904. // check if flood has a set amount of requests and output
  1905. // if not, set the output timer
  1906. if (__self.bets_flood.length === __self.max_requests) {
  1907. do_work();
  1908. } else {
  1909. if (__self.bets_response_reset) {
  1910. __self.bets_response = setTimeout(function () {do_work();}, __self.bets_response_timer);
  1911. } else {
  1912. setTimeout(function () {do_work();}, __self.bets_response_timer);
  1913. }
  1914. }
  1915. });
  1916. };
  1917. Currency.prototype.bets_deduct_bets = function () {
  1918. var __self = this;
  1919. if (__self.bets_viewers.length > 0) {
  1920. // Don't trust the on-the-fly numbers
  1921. for (var i = 0; i < __self.bets_board.length; i++) {
  1922. __self.bets_board[i].num = 0;
  1923. __self.bets_board[i].total = 0;
  1924. }
  1925. __self.bets_total = 0;
  1926. // Remove the points and add them to an escrow
  1927. sql = '';
  1928. for (var i = 0; i < __self.bets_viewers.length; i++) {
  1929. if (__self.bets_viewers[i].amount > 0) {
  1930. if (sql.length > 0) {
  1931. sql += '; ';
  1932. }
  1933. sql += 'UPDATE viewers ';
  1934. sql += 'SET points = points - ' + __self.bets_viewers[i].amount + ' ';
  1935. sql += 'WHERE user = \'' + __self.bets_viewers[i].viewer + '\'';
  1936. __self.bets_board[__self.bets_viewers[i].bet].num += 1;
  1937. __self.bets_board[__self.bets_viewers[i].bet].total += __self.bets_viewers[i].amount;
  1938. __self.bets_total += __self.bets_viewers[i].amount;
  1939. }
  1940. }
  1941. __self.db.execute(sql, function() {});
  1942. }
  1943. };
  1944. Currency.prototype.bets_award_winner = function (winner) {
  1945. var __self = this;
  1946. if (__self.bets_payout) {
  1947. // set payout to complete
  1948. __self.bets_payout = false;
  1949. }
  1950. // Payout the points
  1951. if (__self.bets_viewers.length > 0) {
  1952. sql = '';
  1953. for (var i = 0; i < __self.bets_viewers.length; i++) {
  1954. if (__self.bets_viewers[i].bet == winner.idx) {
  1955. if (sql.length > 0) {
  1956. sql += '; ';
  1957. }
  1958. sql += 'UPDATE viewers ';
  1959. sql += 'SET points = points + ' + Math.ceil(__self.bets_total * __self.bets_viewers[i].amount / __self.bets_board[__self.bets_viewers[i].bet].total) + ' ';
  1960. sql += 'WHERE user = \'' + __self.bets_viewers[i].viewer + '\'';
  1961. }
  1962. }
  1963. __self.db.execute(sql, function() {});
  1964. }
  1965. // Clear the board
  1966. __self.bets_board = [];
  1967. __self.bets_viewers = [];
  1968. __self.bets_total = 0;
  1969. };
  1970. // NEW STUFF
  1971. Currency.prototype.add_quotation = function (data) {
  1972. var __self = this;
  1973. // Chop all the shit out
  1974. data.splice(0, 4);
  1975. data = data.join(' ')
  1976. sql = 'INSERT INTO quotes (`text`) VALUES (' + __self.db.string_escape(data) + ');';
  1977. __self.db.execute(sql, function() {
  1978. __self.irc.emit('message', {message:__self.pre_text + ' New quote added!'});
  1979. });
  1980. };
  1981. Currency.prototype.remove_quotation = function(data) {
  1982. var __self = this;
  1983. // Chop all the shit out
  1984. data.splice(0, 4);
  1985. if (data === parseInt(data, 10)) {
  1986. __self.db.execute('DELETE FROM quotes WHERE id=\''+parseInt(data, 10)+'\';', function(rows) {
  1987. __self.irc.emit('message', {message:__self.pre_text + 'Quote #'+data+' deleted!'});
  1988. });
  1989. }
  1990. };
  1991. Currency.prototype.get_quotation = function() {
  1992. var __self = this;
  1993. // Let's get the number of quotes in database (should store this on its own later) TODO
  1994. __self.db.execute('SELECT COUNT(*) AS quoteCount FROM quotes;', function(rows) {
  1995. var quote_count = rows[0].quoteCount;
  1996. var selected_quote_id = Math.floor(Math.random() * (quote_count - 0 + 1) + 1);
  1997. __self.db.execute('SELECT * FROM quotes WHERE id=\''+selected_quote_id+'\';', function(rows) {
  1998. __self.irc.emit('message', {message:__self.pre_text + rows[0].id + ': ' + rows[0].text});
  1999. });
  2000. });
  2001. };
  2002. module.exports = function (irc, db, commands, options) {
  2003. return new Currency(irc, db, commands, options);
  2004. };