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.

1495 lines
61 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 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. utils = require('./utils.js');
  62. //-------- Construct ---------
  63. function Currency(irc, db, options) {
  64. var __self = this;
  65. __self.irc = irc;
  66. __self.db = db;
  67. // config
  68. __self.config = options || {};
  69. __self.config.currency = options.currency || 'coins';
  70. __self.config.subscribers_json = options.subscribers || '';
  71. __self.config.website = options.website || '';
  72. // general settings
  73. __self.pre_text = '> ' + __self.config.currency + ': ';
  74. __self.max_requests = 10;//response after 10 request
  75. __self.temp = {};
  76. // currency request settings
  77. __self.coin_flood = [];
  78. __self.coin_response = null;
  79. __self.coin_response_timer = 3000;
  80. __self.coin_response_reset = true;
  81. __self.coin_toggle = false;
  82. __self.coin_toggle_msg = null;
  83. __self.coin_toggle_timer = 180000;//milliseconds
  84. // auction settings
  85. __self.auction_status = false;
  86. __self.auction_bids = [];
  87. __self.auction_previous_bid = {};
  88. __self.auction_bid_response = null;
  89. __self.auction_bid_response_long = null;
  90. // handout coins settings
  91. __self.viewer_list = [];
  92. __self.streaming = false;
  93. __self.streaming_check = 4;//minutes
  94. __self.give_coins = false;
  95. __self.give_coins_timer = options.payrate || 30;//minutes
  96. __self.subscriber_check = 4;//minutes
  97. __self.subscribers = [];
  98. // raffle settings
  99. __self.raffle_status = false;
  100. __self.raffle_ticket_requests = [];
  101. __self.raffle_tickets = [];
  102. __self.raffle_ticket_cost = 10;//currency cost per ticket
  103. __self.raffle_max_tickets = 10;
  104. // raffle restoration
  105. __self.raffle_restore_ticket_requests = [];
  106. __self.raffle_restore_tickets = [];
  107. // gambling settings
  108. __self.bets_status = false;
  109. __self.bets_board = [];
  110. __self.bets_viewers = [];
  111. __self.bets_payout = false;
  112. __self.bets_total = 0;
  113. __self.bets_flood = [];
  114. __self.bets_response = null;
  115. __self.bets_response_timer = 3000;
  116. __self.bets_response_reset = true;
  117. }
  118. //-------- Methods ---------
  119. Currency.prototype.start = function () {
  120. var __self = this;
  121. __self.handout_coins();
  122. };
  123. Currency.prototype.commands = function (data) {
  124. var __self = this,
  125. 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(),
  126. moderator_initiated = __self.irc.mods.indexOf(__self.irc.caller(data[0])) > 0;
  127. // handle !<currency> commands
  128. if (data[3].slice(1) === '!' + __self.config.currency.toLowerCase()) {
  129. // public commands
  130. if (!__self.coin_toggle && data[4] === undefined) {
  131. __self.get_coins(__self.irc.caller(data[0]));
  132. }
  133. // broadcaster only commands
  134. if (broadcaster_bot_initiated) {
  135. //open / close auction system
  136. if (data[4] === 'auction') {
  137. switch (data[5]) {
  138. case 'open':
  139. __self.auction(true);
  140. break;
  141. case 'close':
  142. __self.auction(false);
  143. break;
  144. case 'draw':
  145. __self.next_auction_winner();
  146. break;
  147. case 'cancel':
  148. __self.auction('cancel');
  149. break;
  150. }
  151. }
  152. // open / close raffle system
  153. if (data[4] === 'raffle') {
  154. switch (data[5]) {
  155. case 'open':
  156. if (data[6] && data[7] && !__self.raffle_status) {
  157. if(parseInt(data[6], 10) > 0 && parseInt(data[7], 10) > 0) {
  158. // save default values
  159. __self.temp.raffle_ticket_cost = __self.raffle_ticket_cost;
  160. __self.temp.raffle_max_tickets = __self.raffle_max_tickets;
  161. // set new raffle cost / amount
  162. __self.raffle_ticket_cost = data[6];
  163. __self.raffle_max_tickets = data[7];
  164. }
  165. } else if (__self.temp.raffle_ticket_cost && __self.temp.raffle_max_tickets && !__self.raffle_status){
  166. __self.raffle_ticket_cost = __self.temp.raffle_ticket_cost;
  167. __self.raffle_max_tickets = __self.temp.raffle_max_tickets;
  168. delete __self.temp.raffle_ticket_cost;
  169. delete __self.temp.raffle_max_tickets;
  170. }
  171. __self.raffle(true);
  172. break;
  173. case 'close':
  174. __self.raffle(false);
  175. break;
  176. case 'draw':
  177. __self.next_raffle_winner();
  178. break;
  179. case 'cancel':
  180. __self.raffle('cancel');
  181. break;
  182. case 'restore':
  183. __self.raffle('restore');
  184. break;
  185. }
  186. }
  187. // open / close betting system
  188. if (data[4] === 'bet') {
  189. switch (data[5]) {
  190. case 'open':
  191. if (data[5] && data[6]) {
  192. __self.bets(true, data);
  193. } else {
  194. __self.irc.emit('message', {message:__self.pre_text + 'Unable to open betting, need at least two items to bet against'});
  195. }
  196. break;
  197. case 'close':
  198. __self.bets(false, null);
  199. break;
  200. case 'pool':
  201. __self.bets('pool', null);
  202. break;
  203. case 'winner':
  204. __self.bets('winner', data[6]);
  205. break;
  206. }
  207. }
  208. // add currency
  209. if (data[4] === 'add') {
  210. if(parseInt(data[5], 10) > 0 && data[6]) {
  211. __self.adjust_currency('add', data[5], data[6]);
  212. }
  213. }
  214. // remove currency
  215. if (data[4] === 'remove') {
  216. if(parseInt(data[5], 10) > 0 && data[6]) {
  217. __self.adjust_currency('remove', data[5], data[6]);
  218. }
  219. }
  220. // push currency to new viewer
  221. if (data[4] === 'push') {
  222. if(parseInt(data[5], 10) > 0 && data[6]) {
  223. __self.adjust_currency('push', data[5], data[6]);
  224. }
  225. }
  226. }
  227. // moderator commands
  228. if (broadcaster_bot_initiated || moderator_initiated) {
  229. // enable/disable currency requests
  230. switch (data[4]) {
  231. case 'on':
  232. if (!__self.auction_status && !__self.raffle_status) {
  233. __self.coin_toggle = false;
  234. // output currency request status
  235. __self.irc.emit('message', {message:__self.pre_text + 'Currency requests are now enabled. Type !' + __self.config.currency.toLowerCase() + ' to view your total'});
  236. // stop periodic message
  237. clearInterval(__self.coin_toggle_msg);
  238. }
  239. break;
  240. case 'off':
  241. if (!__self.auction_status && !__self.raffle_status) {
  242. var msg;
  243. if (!__self.coin_toggle) {
  244. // output message depending on if an offsite is provided
  245. if (__self.config.website !== '') {
  246. msg = __self.pre_text + 'Currency requests have been disabled. To view your ' + __self.config.currency + ' please visit ' + __self.config.website;
  247. __self.irc.emit('message', {message:msg});
  248. } else {
  249. msg = __self.pre_text + 'Currency requests have been disabled';
  250. __self.irc.emit('message', {message:msg});
  251. }
  252. }
  253. // start the periodic message
  254. if (data[5] !== undefined && data[6] !== undefined) {
  255. // manually enable / disable repeat
  256. if (data[5] === 'repeat') {
  257. switch (data[6]) {
  258. case 'on':
  259. __self.irc.emit('message', {message:'+ Periodic notification enabled'});
  260. __self.coin_toggle_msg = setInterval(function () {
  261. if (__self.coin_toggle) {
  262. msg = __self.pre_text + 'To view your ' + __self.config.currency + ' please visit ' + __self.config.website;
  263. __self.irc.emit('message', {message:msg});
  264. }
  265. }, __self.coin_toggle_timer);
  266. break;
  267. case 'off':
  268. __self.irc.emit('message', {message:'+ Periodic notification disabled'});
  269. clearInterval(__self.coin_toggle_msg);
  270. }
  271. }
  272. }
  273. __self.coin_toggle = true;
  274. }
  275. break;
  276. }
  277. // adjust currency response rate
  278. if (data[4] === 'timer') {
  279. if (isNaN(parseInt(data[5], 10)) === false) {
  280. if (data[5] >= 3 && data[5] % 1 === 0) {
  281. __self.coin_response_timer = data[5] * 1000;
  282. __self.irc.emit('message', {message:__self.pre_text + 'Currency totals will now show ' + data[5] + ' seconds after request'});
  283. if (data[6] && data[7]) {
  284. if (data[6] === 'reset') {
  285. switch(data[7]) {
  286. case 'on':
  287. __self.irc.emit('message', {message:'+ Timer will now reset after each new request'});
  288. __self.coin_response_reset = true;
  289. break;
  290. case 'off':
  291. __self.irc.emit('message', {message:'+ Timer will not reset after each new request'});
  292. __self.coin_response_reset = false;
  293. break;
  294. }
  295. }
  296. }
  297. } else if (data[5] < 3) {
  298. __self.irc.emit('message', {message:__self.pre_text + 'Timer cannot be less than 2 seconds'});
  299. }
  300. }
  301. }
  302. }
  303. }
  304. // public commands related to !<currency>
  305. switch (data[3].slice(1)) {
  306. // submit bid for the auction
  307. case '!bid':
  308. if (isNaN(parseInt(data[4], 10)) === false) {
  309. if (data[4] > 0 && data[4] % 1 === 0) {
  310. __self.bid(__self.irc.caller(data[0]), parseInt(data[4], 10));
  311. }
  312. }
  313. break;
  314. // purchase a ticket for raffle
  315. case '!ticket':
  316. if (isNaN(parseInt(data[4], 10)) === false) {
  317. if (data[4] >= 0 && data[4] % 1 === 0) {
  318. __self.collect_tickets(__self.irc.caller(data[0]), parseInt(data[4], 10));
  319. }
  320. }
  321. break;
  322. }
  323. // place a bet
  324. if (__self.bets_status === true) {
  325. for (var i = 0; i < __self.bets_board.length; i++) {
  326. if (data[3].slice(1) === '!' + __self.bets_board[i].name) {
  327. if (isNaN(parseInt(data[4], 10)) === false) {
  328. if (data[4] >= 0 && data[4] % 1 === 0) {
  329. __self.collect_bets(__self.irc.caller(data[0]), __self.bets_board[i], parseInt(data[4], 10));
  330. break;
  331. }
  332. }
  333. }
  334. }
  335. }
  336. };
  337. /**
  338. * ============================================
  339. * CURRENCY REQUESTS
  340. * --------------------------------------------
  341. */
  342. Currency.prototype.get_coins = function (caller) {
  343. var __self = this;
  344. function fill_request(viewer, points) {
  345. var request = '(' + points + ')';
  346. if (__self.raffle_status) {
  347. for (var i = 0; i < __self.raffle_ticket_requests.length; i++){
  348. if (__self.raffle_ticket_requests[i].viewer.toLowerCase() === viewer.toLowerCase() && (__self.raffle_ticket_requests[i].tickets * __self.raffle_ticket_cost) <= points) {
  349. request = '(' + (points - (__self.raffle_ticket_requests[i].tickets * __self.raffle_ticket_cost)) + ') [' + __self.raffle_ticket_requests[i].tickets + ']';
  350. break;
  351. }
  352. }
  353. return request;
  354. } else {
  355. return request;
  356. }
  357. }
  358. function do_work() {
  359. var multi_response = '';
  360. if (__self.coin_flood.length > 1) {// send flood requests
  361. __self.query_coins(__self.coin_flood, function (rows) {
  362. for (var i = 0; i < rows.length; i++) {
  363. var currency_request = fill_request(rows[i].user, rows[i].points);
  364. // setup currency response
  365. if (i !== rows.length - 1) {
  366. multi_response += rows[i].user + ' ' + currency_request + ', ';
  367. } else {
  368. multi_response += rows[i].user + ' ' + currency_request;
  369. }
  370. }
  371. __self.irc.emit('message', {message:__self.pre_text + multi_response, timer: 1});
  372. });
  373. } else if (__self.coin_flood.length === 1) {// send single request
  374. __self.query_coins(caller, function (rows) {
  375. var currency_request = fill_request(rows[0].user, rows[0].points);
  376. __self.irc.emit('message', {message:__self.pre_text + caller + ' ' + currency_request, timer: 1});
  377. });
  378. }
  379. // clear flood requests
  380. __self.coin_flood = [];
  381. }
  382. // add flood users to array
  383. if (__self.coin_flood.indexOf(caller) < 0) {
  384. __self.coin_flood.push(caller);
  385. }
  386. // clear timer on flood
  387. if (__self.coin_response_reset) {
  388. clearTimeout(__self.coin_response);
  389. }
  390. // check if flood has a set amount of requests and output
  391. // if not, set the output timer
  392. if (__self.coin_flood.length === __self.max_requests) {
  393. do_work();
  394. } else {
  395. if (__self.coin_response_reset) {
  396. __self.coin_response = setTimeout(function () {do_work();}, __self.coin_response_timer);
  397. } else {
  398. setTimeout(function () {do_work();}, __self.coin_response_timer);
  399. }
  400. }
  401. };
  402. Currency.prototype.query_coins = function (data, callback) {
  403. var __self = this, sql = '';
  404. // build sql conditions
  405. if (typeof data === 'string') {
  406. sql = 'SELECT * FROM viewers WHERE ' + 'user = \'' + data.toLowerCase() + '\'';
  407. } else {
  408. for (var i = 0; i < data.length; i++) {
  409. if (i !== data.length - 1) {
  410. sql += 'SELECT * FROM viewers WHERE ' + 'user = \'' + data[i].toLowerCase() + '\'' + ';';
  411. } else {
  412. sql += 'SELECT * FROM viewers WHERE ' + 'user = \'' + data[i].toLowerCase() + '\'';
  413. }
  414. }
  415. }
  416. // execute query
  417. __self.db.execute(sql, function (rows) {
  418. var temp = [], newrows = [];
  419. if (typeof data !== 'string') {
  420. // get rid of the nested arrays
  421. for (var i = 0; i < rows.length; i++) {
  422. if (rows[i].length > 0) {
  423. newrows.push(rows[i][0]);
  424. }
  425. }
  426. // separate users into their own array
  427. for (var i = 0; i < newrows.length; i++) {
  428. temp.push(newrows[i].user.charAt(0).toUpperCase() + newrows[i].user.slice(1));
  429. }
  430. // compare the users in the data array against the temp array
  431. // if not found, push them to rows with 0 points
  432. for (var i = 0; i < data.length; i++) {
  433. if (temp.indexOf(data[i]) < 0) {
  434. newrows.push({'user' : data[i], 'points' : 0});
  435. }
  436. }
  437. // capitalize usernames on rows
  438. for (var key in newrows) {
  439. if (newrows.hasOwnProperty(key)) {
  440. newrows[key].user = newrows[key].user.charAt(0).toUpperCase() + newrows[key].user.slice(1);
  441. }
  442. }
  443. rows = newrows;
  444. } else {
  445. if (rows.length === 0) {
  446. rows = [{'user' : data, 'points' : 0}];
  447. } else {
  448. rows[0].user = rows[0].user.charAt(0).toUpperCase() + rows[0].user.slice(1);
  449. }
  450. }
  451. callback(rows);
  452. });
  453. };
  454. /**
  455. * ============================================
  456. * HANDOUT CURRENCY
  457. * --------------------------------------------
  458. */
  459. Currency.prototype.handout_coins = function () {
  460. var __self = this;
  461. // check stream status
  462. function stream_status() {
  463. var time = utils.make_interval(__self.streaming_check);
  464. if (time === 0) {
  465. // get stream status
  466. https.get('https://api.twitch.tv/kraken/streams/' + __self.irc.config.channel.slice(1), function (response) {
  467. var body = '';
  468. // put together response
  469. response.on('data', function (chunk) {
  470. body += chunk;
  471. });
  472. // start / stop handing out coins based on stream status
  473. response.on('end', function () {
  474. var json = JSON.parse(body);
  475. __self.streaming = json.stream !== null;
  476. if (__self.streaming && __self.give_coins === false) {
  477. insert_coins();
  478. }
  479. __self.irc.emit('data', 'DATA - Online Status Check - Returned: ' + __self.streaming);
  480. setTimeout(stream_status, 1000);
  481. });
  482. });
  483. } else {
  484. setTimeout(stream_status, time);
  485. }
  486. }
  487. // get subscribers
  488. function subscribers() {
  489. var time = utils.make_interval(__self.subscriber_check);
  490. if (time === 0) {
  491. // get stream status
  492. http.get(__self.config.subscribers_json, function (response) {
  493. var body = '';
  494. // put together response
  495. response.on('data', function (chunk) {
  496. body += chunk;
  497. });
  498. // start / stop handing out coins based on stream status
  499. response.on('end', function () {
  500. var json = JSON.parse(body);
  501. var entries = json.feed.entry, subs = '';
  502. __self.subscribers = [];
  503. for (var i = 0; i < entries.length; i++) {
  504. __self.subscribers.push(entries[i].title['$t']);
  505. subs += entries[i].title['$t'] + ' ';
  506. }
  507. __self.irc.emit('data', 'DATA - Subscriber Check - Returned: ' + subs);
  508. setTimeout(subscribers, 1000);
  509. });
  510. });
  511. } else {
  512. setTimeout(subscribers, time);
  513. }
  514. }
  515. // trigger coin handout
  516. function insert_coins() {
  517. __self.give_coins = __self.streaming;
  518. if (__self.give_coins) {
  519. var time = utils.make_interval(__self.give_coins_timer);
  520. if (time === 0) {
  521. __self.irc.raw('WHO ' + __self.irc.config.channel);
  522. setTimeout(insert_coins, 1000);
  523. } else {
  524. setTimeout(insert_coins, time);
  525. }
  526. }
  527. }
  528. // monitor viewers in irc
  529. __self.irc.on('data', function (data) {
  530. if (__self.streaming) {
  531. var data_split = data.split(' '), viewer = '';
  532. // viewers from \who
  533. if (data_split[3] == '352') {
  534. if (data_split[6] !== undefined) {
  535. viewer = data_split[6].toLowerCase();
  536. if (__self.viewer_list.indexOf(viewer) < 0) {
  537. __self.viewer_list.push(viewer);
  538. }
  539. }
  540. }
  541. // viewers chatting
  542. if (data_split[3] == 'PRIVMSG') {
  543. var servernick = data_split[2].toLowerCase().split('!');
  544. viewer = servernick[0];
  545. if (viewer != __self.irc.config.name.toLowerCase()) {
  546. viewer = viewer.slice(1);
  547. }
  548. if (__self.viewer_list.indexOf(viewer) < 0) {
  549. __self.viewer_list.push(viewer);
  550. }
  551. }
  552. // give coins after \who and handout_coins is true
  553. if (__self.give_coins && data_split[3] == '315') {
  554. var clone_viewer_list = __self.viewer_list;
  555. // clear old list and start recording
  556. __self.viewer_list = [];
  557. // build sql from the saved viewer list
  558. var sql = '';
  559. for (var i = 0; i < clone_viewer_list.length; i++) {
  560. var currency_amount = __self.subscribers.indexOf(clone_viewer_list[i]) >= 0 ? 2 : 1;
  561. if (clone_viewer_list[i] !== '') {
  562. if (i != clone_viewer_list.length - 1) {
  563. sql += 'INSERT INTO viewers (user, points) ';
  564. sql += 'VALUES (\'' + clone_viewer_list[i] + '\', ' + currency_amount + ') ';
  565. sql += 'ON DUPLICATE KEY UPDATE points = points + ' + currency_amount + '; ';
  566. } else {
  567. sql += 'INSERT INTO viewers (user, points) ';
  568. sql += 'VALUES (\'' + clone_viewer_list[i] + '\', ' + currency_amount + ') ';
  569. sql += 'ON DUPLICATE KEY UPDATE points = points + ' + currency_amount;
  570. }
  571. }
  572. }
  573. // execute query
  574. __self.db.execute(sql, function () {});
  575. }
  576. } else {
  577. __self.viewer_list = [];
  578. }
  579. });
  580. stream_status();
  581. // only start subscribers if gdoc is available
  582. if (__self.config.subscribers_json !== '') {
  583. subscribers();
  584. }
  585. };
  586. /**
  587. * ============================================
  588. * Adjust Currency
  589. * --------------------------------------------
  590. */
  591. Currency.prototype.adjust_currency = function (method, amount, viewer) {
  592. var __self = this;
  593. viewer = viewer.toLowerCase();
  594. __self.db.execute('SELECT * FROM viewers WHERE user=\'' + viewer + '\'', function(rows){
  595. if (rows.length > 0 || method === 'push') {
  596. var check = rows.length > 0 ? rows[0].user : rows.push({user: viewer});
  597. if (check === viewer || method === 'push') {
  598. var sql = '', settings = [];
  599. // push settings for message
  600. if (method === 'add' || method === 'push') {
  601. settings.push('+');
  602. settings.push('Added');
  603. settings.push('to');
  604. } else if (method === 'remove') {
  605. settings.push('-');
  606. settings.push('Removed');
  607. settings.push('from');
  608. }
  609. settings.push(rows[0].user.charAt(0).toUpperCase() + rows[0].user.slice(1));
  610. // create sql
  611. if (method === 'add' || method === 'remove') {
  612. sql += 'UPDATE viewers ';
  613. sql += 'SET points = points ' + settings[0] + ' ' + amount + ' ';
  614. sql += 'WHERE user = \'' + rows[0].user + '\'; ';
  615. } else if (method === 'push') {
  616. sql += 'INSERT INTO viewers (user, points) ';
  617. sql += 'VALUES (\'' + viewer + '\', ' + amount + ') ';
  618. sql += 'ON DUPLICATE KEY UPDATE points = points + ' + amount + '; ';
  619. }
  620. //execute adjustment
  621. __self.db.execute(sql, function(){
  622. __self.irc.emit('message', {message:__self.pre_text + settings[1] + ' ' + amount + ' ' + __self.config.currency + ' ' + settings[2] + ' ' + settings[3]});
  623. });
  624. }
  625. } else {
  626. __self.irc.emit('message', {message:__self.pre_text + 'User was not found, use the push command to add a new user'});
  627. }
  628. });
  629. };
  630. /**
  631. * ============================================
  632. * AUCTION SYSTEM
  633. * --------------------------------------------
  634. */
  635. Currency.prototype.auction = function (status) {
  636. var __self = this;
  637. switch (status) {
  638. case true:
  639. if (!__self.bets_status) {
  640. if (!__self.raffle_status) {
  641. if (!__self.auction_status) {
  642. // open up the auction
  643. __self.auction_status = true;
  644. // request toggle
  645. if (__self.temp.raffle_toggle) {
  646. __self.temp.auction_toggle = __self.temp.raffle_toggle;
  647. } else {
  648. __self.temp.auction_toggle = __self.coin_toggle;
  649. }
  650. __self.coin_toggle = false;
  651. // default request timer
  652. if (__self.temp.raffle_timer && __self.temp.raffle_timer_reset) {
  653. __self.temp.auction_timer = __self.temp.raffle_timer;
  654. __self.temp.auction_timer_reset = __self.temp.raffle_timer_reset;
  655. } else {
  656. __self.temp.auction_timer = __self.coin_response_timer;
  657. __self.temp.auction_timer_reset = __self.coin_response_reset;
  658. }
  659. __self.coin_response_timer = 3000;
  660. __self.coin_response_reset = true;
  661. // clear previous bids
  662. __self.auction_bids = [];
  663. __self.auction_previous_bid = {};
  664. // auction open response
  665. __self.irc.emit('message', {message:__self.pre_text + 'Auction opened, accepting bids'})
  666. } else {
  667. // auction is already open response
  668. __self.irc.emit('message', {message:__self.pre_text + 'Auction already in progress'});
  669. }
  670. } else {
  671. // raffle currently running
  672. __self.irc.emit('message', {message:__self.pre_text + 'You must close the raffle before you can open an auction'});
  673. }
  674. } else {
  675. // gambling currently running
  676. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open an auction'});
  677. }
  678. break;
  679. case false:
  680. if (__self.auction_status) {
  681. // close the auction
  682. __self.auction_status = false;
  683. // request toggle
  684. __self.coin_toggle = __self.temp.auction_toggle;
  685. delete __self.temp.auction_toggle;
  686. // default request timer
  687. __self.coin_response_timer = __self.temp.auction_timer;
  688. __self.coin_response_reset = __self.temp.auction_timer_reset;
  689. delete __self.temp.auction_timer;
  690. delete __self.temp.auction_timer_reset;
  691. // clear response timers
  692. clearTimeout(__self.auction_bid_response);
  693. clearInterval(__self.auction_bid_response_long);
  694. if (__self.auction_bids.length > 0) {
  695. // pick a winner response
  696. for (var i = 0; i < __self.auction_bids.length; i++) {
  697. if (__self.auction_bids[i].bid === utils.max(__self.auction_bids)) {
  698. __self.irc.emit('message', {message:__self.pre_text + 'Auction closed, Winner: ' + __self.auction_bids[i].viewer + ' @ ' + __self.auction_bids[i].bid});
  699. // save the winners info for draw refund
  700. __self.auction_previous_bid.viewer = __self.auction_bids[i].viewer;
  701. __self.auction_previous_bid.bid = __self.auction_bids[i].bid;
  702. // remove winners money
  703. var sql = '';
  704. sql += 'UPDATE viewers ';
  705. sql += 'SET points = points - ' + __self.auction_bids[i].bid + ' ';
  706. sql += 'WHERE user = \'' + __self.auction_bids[i].viewer + '\'';
  707. __self.db.execute(sql, function() {});
  708. // remove winner from main list
  709. __self.auction_bids.splice(i, 1);
  710. break;
  711. }
  712. }
  713. } else {
  714. // no bidders to pick from response
  715. __self.irc.emit('message', {message:__self.pre_text + 'Auction closed, no bidders to pick a winner'});
  716. }
  717. } else {
  718. // auction is already open response
  719. __self.irc.emit('message', {message:__self.pre_text + 'Auction is already closed'});
  720. }
  721. break;
  722. case 'cancel':
  723. if (__self.auction_status) {
  724. // close the auction
  725. __self.auction_status = false;
  726. // request toggle
  727. __self.coin_toggle = __self.temp.auction_toggle;
  728. delete __self.temp.auction_toggle;
  729. // default request timer
  730. __self.coin_response_timer = __self.temp.auction_timer;
  731. __self.coin_response_reset = __self.temp.auction_timer_reset;
  732. delete __self.temp.auction_timer;
  733. delete __self.temp.auction_timer_reset;
  734. // clear response timers
  735. clearTimeout(__self.auction_bid_response);
  736. clearInterval(__self.auction_bid_response_long);
  737. // clear previous bids
  738. __self.auction_bids = [];
  739. __self.auction_previous_bid = {};
  740. // auction cancelled notification
  741. __self.irc.emit('message', {message:__self.pre_text + 'Auction has been cancelled'});
  742. } else {
  743. // auction cancelled notification
  744. __self.irc.emit('message', {message:__self.pre_text + 'Auction is not opened'});
  745. }
  746. break;
  747. }
  748. };
  749. Currency.prototype.bid = function (caller, amount) {
  750. var __self = this;
  751. function find_duplicate(amount) {
  752. var duplicate = false;
  753. for (var i = 0; i < __self.auction_bids.length; i++) {
  754. if (__self.auction_bids[i].bid === amount) {
  755. duplicate = true;
  756. break;
  757. }
  758. }
  759. return duplicate;
  760. }
  761. if (__self.auction_status) {
  762. // verify that bidder has the coins for bidding
  763. __self.query_coins(caller, function (rows) {
  764. var has_tickets = false;
  765. // only add bid if they have the enough to pay
  766. if (rows[0].points >= amount) {
  767. if (__self.auction_bids.length > 0) {
  768. // check if an existing bid exists and modify it
  769. for (var i = 0; i < __self.auction_bids.length; i++) {
  770. if (__self.auction_bids[i].viewer === caller) {
  771. has_tickets = true;
  772. // check if bid is higher then original and not a duplicate
  773. if (__self.auction_bids[i].bid < amount && !find_duplicate(amount)) {
  774. __self.auction_bids[i].bid = amount;
  775. }
  776. break;
  777. }
  778. }
  779. // add new bids to list if they are not a duplicate
  780. if (!has_tickets && !find_duplicate(amount)) {
  781. __self.auction_bids.push({viewer: caller, bid: amount});
  782. }
  783. } else {
  784. // push first bid
  785. __self.auction_bids.push({viewer: caller, bid: amount});
  786. }
  787. }
  788. // clear timers on flood
  789. clearTimeout(__self.auction_bid_response);
  790. clearInterval(__self.auction_bid_response_long);
  791. // reply after set amount of bids
  792. if ((__self.auction_bids.length % __self.max_requests) === 0) {
  793. // bulk flood response
  794. for (var i = 0; i < __self.auction_bids.length; i++) {
  795. if (__self.auction_bids[i].bid === utils.max(__self.auction_bids)) {
  796. __self.irc.msg(__self.pre_text + 'Highest bid, ' + __self.auction_bids[i].viewer + ' @ ' + __self.auction_bids[i].bid);
  797. }
  798. }
  799. } else {
  800. // response after time without flood has passed
  801. var viewer, bid;
  802. for (var i = 0; i < __self.auction_bids.length; i++) {
  803. if (__self.auction_bids[i].bid === utils.max(__self.auction_bids)) {
  804. viewer = __self.auction_bids[i].viewer;
  805. bid = __self.auction_bids[i].bid;
  806. }
  807. }
  808. if (viewer !== undefined && bid !== undefined && __self.auction_status) {
  809. var msg = __self.pre_text + 'Highest bid, ' + viewer + ' @ ' + bid;
  810. __self.auction_bid_response = setTimeout(function () {__self.irc.emit('message', {message:msg, timer: 1});}, 5000);
  811. __self.auction_bid_response_long = setInterval(function () {__self.irc.emit('message', {message:msg, timer: 1});}, 30000);
  812. }
  813. }
  814. });
  815. }
  816. };
  817. Currency.prototype.next_auction_winner = function () {
  818. var __self = this, empty_list = [];
  819. // custom dialog when the bidder list is empty
  820. empty_list.push('Hey, I just met you and this is crazy, but there\'s no more bidders, so start an new auction maybe?');
  821. empty_list.push('Are there more bidders? Well, to tell you the truth, in all this excitement I kind of lost track myself.');
  822. empty_list.push('Heyyyyyy there\'s no more bidders, Op, op, op, op, Open Auction Style.');
  823. empty_list.push('Da bids! Da bids! Where are all da bids, boss?');
  824. if (!__self.auction_status) {
  825. // get next highest bidder or prompt to open new auction
  826. if (__self.auction_bids.length > 0) {
  827. for (var i = 0; i < __self.auction_bids.length; i++) {
  828. if (__self.auction_bids[i].bid === utils.max(__self.auction_bids)) {
  829. __self.irc.emit('message',{message:__self.pre_text + 'Drawing the next highest bid: ' + __self.auction_bids[i].viewer + ' @ ' + __self.auction_bids[i].bid});
  830. // refund previous winner's money
  831. var sql = '';
  832. sql += 'UPDATE viewers ';
  833. sql += 'SET points = points + ' + __self.auction_previous_bid.bid + ' ';
  834. sql += 'WHERE user = \'' + __self.auction_previous_bid.viewer + '\'';
  835. __self.db.execute(sql, function() {});
  836. // save the new winner's info for next draw
  837. __self.auction_previous_bid.viewer = __self.auction_bids[i].viewer;
  838. __self.auction_previous_bid.bid = __self.auction_bids[i].bid;
  839. // remove winners money
  840. sql = '';
  841. sql += 'UPDATE viewers ';
  842. sql += 'SET points = points - ' + __self.auction_bids[i].bid + ' ';
  843. sql += 'WHERE user = \'' + __self.auction_bids[i].viewer + '\'';
  844. __self.db.execute(sql, function() {});
  845. // remove winner from main list
  846. __self.auction_bids.splice(i, 1);
  847. break;
  848. }
  849. }
  850. } else {
  851. // check if a previous viewer is saved
  852. if (__self.auction_previous_bid.viewer !== null) {
  853. // refund previous winner's money
  854. var sql = '';
  855. sql += 'UPDATE viewers ';
  856. sql += 'SET points = points + ' + __self.auction_previous_bid.bid + ' ';
  857. sql += 'WHERE user = \'' + __self.auction_previous_bid.viewer + '\'';
  858. __self.db.execute(sql, function() {});
  859. // clear previous bid
  860. __self.auction_previous_bid = {};
  861. }
  862. // notify that there's no more bids
  863. __self.irc.emit('message',{message:__self.pre_text + utils.selectRandomArrayItem(empty_list)});
  864. }
  865. }
  866. };
  867. /**
  868. * ============================================
  869. * RAFFLE SYSTEM
  870. * --------------------------------------------
  871. */
  872. Currency.prototype.raffle = function (status) {
  873. var __self = this;
  874. switch (status) {
  875. case true:
  876. if (!__self.bets_status) {
  877. if (!__self.auction_status) {
  878. if (!__self.raffle_status) {
  879. // open up a raffle
  880. __self.raffle_status = true;
  881. // request toggle
  882. if (__self.temp.auction_toggle) {
  883. __self.temp.raffle_toggle = __self.temp.auction_toggle;
  884. } else {
  885. __self.temp.raffle_toggle = __self.coin_toggle;
  886. }
  887. __self.coin_toggle = false;
  888. // default request timer
  889. if (__self.temp.auction_timer && __self.temp.auction_timer_reset) {
  890. __self.temp.raffle_timer = __self.temp.auction_timer;
  891. __self.temp.raffle_timer_reset = __self.temp.auction_timer_reset;
  892. } else {
  893. __self.temp.raffle_timer = __self.coin_response_timer;
  894. __self.temp.raffle_timer_reset = __self.coin_response_reset;
  895. }
  896. __self.coin_response_timer = 3000;
  897. __self.coin_response_reset = true;
  898. // save previous raffle settings in case
  899. // a new one is opened on accident
  900. __self.raffle_restore_ticket_requests = __self.raffle_ticket_requests;
  901. __self.raffle_restore_tickets = __self.raffle_tickets;
  902. // clear previous tickets
  903. __self.raffle_ticket_requests = [];
  904. __self.raffle_tickets = [];
  905. // raffle open response
  906. __self.irc.emit('message',{message:__self.pre_text + 'Raffle opened'});
  907. __self.irc.emit('message',{message:'+ Tickets cost ' + __self.raffle_ticket_cost + ' ' + __self.config.currency.toLowerCase() + ' / Maximum of ' + __self.raffle_max_tickets + ' tickets per viewer'});
  908. } else {
  909. // raffle in progress response
  910. __self.irc.emit('message',{message:__self.pre_text + 'Raffle already in progress'});
  911. }
  912. } else {
  913. // auction in progress
  914. __self.irc.emit('message', {message:__self.pre_text + 'You must close the auction before you can open an a raffle'});
  915. }
  916. } else {
  917. // gambling currently running
  918. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open a raffle'});
  919. }
  920. break;
  921. case false:
  922. if (__self.raffle_status) {
  923. // close the raffle
  924. __self.raffle_status = false;
  925. // request toggle
  926. __self.coin_toggle = __self.temp.raffle_toggle;
  927. delete __self.temp.raffle_toggle;
  928. // default request timer
  929. __self.coin_response_timer = __self.temp.raffle_timer;
  930. __self.coin_response_reset = __self.temp.raffle_timer_reset;
  931. delete __self.temp.raffle_timer;
  932. delete __self.temp.raffle_timer_reset;
  933. // validation / winner / deduction
  934. __self.raffle_winner();
  935. } else {
  936. // raffle is already open response
  937. __self.irc.emit('message',{message:__self.pre_text + 'Raffle is already closed'});
  938. }
  939. break;
  940. case 'cancel':
  941. if (__self.raffle_status) {
  942. // close the raffle
  943. __self.raffle_status = false;
  944. // request toggle
  945. __self.coin_toggle = __self.temp.raffle_toggle;
  946. delete __self.temp.raffle_toggle;
  947. // default request timer
  948. __self.coin_response_timer = __self.temp.raffle_timer;
  949. __self.coin_response_reset = __self.temp.raffle_timer_reset;
  950. delete __self.temp.raffle_timer;
  951. delete __self.temp.raffle_timer_reset;
  952. // clear previous tickets
  953. __self.raffle_ticket_requests = [];
  954. __self.raffle_tickets = [];
  955. // raffle cancelled notification
  956. __self.irc.emit('message', {message:__self.pre_text + 'Raffle has been cancelled'});
  957. } else {
  958. // raffle cancelled notification
  959. __self.irc.emit('message', {message:__self.pre_text + 'Raffle is not opened'});
  960. }
  961. break;
  962. case 'restore':
  963. if (__self.raffle_status) {
  964. // close raffle
  965. __self.raffle_status = false;
  966. // restore previous raffle tickets
  967. __self.raffle_ticket_requests = __self.raffle_restore_ticket_requests;
  968. __self.raffle_tickets = __self.raffle_restore_tickets;
  969. __self.irc.emit('message', {message:__self.pre_text + 'Previous raffle has been restored'});
  970. } else {
  971. // raffle restore failed notification
  972. __self.irc.emit('message', {message:__self.pre_text + 'Raffle is closed, unable to restore'});
  973. }
  974. break;
  975. }
  976. };
  977. Currency.prototype.collect_tickets = function (caller, amount) {
  978. var __self = this, has_tickets = false;
  979. if (__self.raffle_ticket_requests.length > 0) {
  980. // check if viewer already has tickets
  981. for (var i = 0; i < __self.raffle_ticket_requests.length; i++) {
  982. if (__self.raffle_ticket_requests[i].viewer === caller) {
  983. has_tickets = true;
  984. if (amount <= __self.raffle_max_tickets && amount >= 1) {
  985. __self.raffle_ticket_requests[i].tickets = amount;
  986. } else if (amount === 0) {
  987. __self.raffle_ticket_requests.splice(i, 1);
  988. }
  989. break;
  990. }
  991. }
  992. // if viewer doesn't have tickets and meets > 1 < max req add their request
  993. if (!has_tickets && amount <= __self.raffle_max_tickets && amount >= 1 && amount !== 0) {
  994. __self.raffle_ticket_requests.push({viewer: caller, tickets: amount});
  995. }
  996. } else {
  997. // push first ticket if > 1 < max
  998. if (amount <= __self.raffle_max_tickets && amount >= 1 && amount !== 0) {
  999. __self.raffle_ticket_requests.push({viewer: caller, tickets: amount});
  1000. }
  1001. }
  1002. };
  1003. Currency.prototype.raffle_winner = function () {
  1004. var __self = this, sql = '';
  1005. if (__self.raffle_ticket_requests.length > 0) {
  1006. // setup sql to grab all viewers that request coins from the database
  1007. sql += 'SELECT * FROM viewers WHERE ';
  1008. for (var i = 0; i < __self.raffle_ticket_requests.length; i++) {
  1009. if (i !== __self.raffle_ticket_requests.length - 1) {
  1010. sql += 'user=\'' + __self.raffle_ticket_requests[i].viewer.toLowerCase() + '\' OR ';
  1011. } else {
  1012. sql += 'user=\'' + __self.raffle_ticket_requests[i].viewer.toLowerCase() + '\'';
  1013. }
  1014. }
  1015. // execute viewer search query
  1016. __self.db.execute(sql, function(rows) {
  1017. // currency validation
  1018. // - this takes the results of the query and uses the names from the database
  1019. // to filter through the viewers that requested tickets (since they have to be in the
  1020. // database in the first place)
  1021. // - during the filtering process the viewers requested tickets are multiplied by the
  1022. // ticket cost and compared against their currency amount
  1023. // - if the viewer has the funds, their tickets are added and the sql is updated to include their
  1024. // deduction
  1025. sql = '';
  1026. for (var i = 0; i < rows.length; i++) {
  1027. for (var j = 0; j < __self.raffle_ticket_requests.length; j++) {
  1028. if (__self.raffle_ticket_requests[j].viewer.toLowerCase() === rows[i].user) {
  1029. var money = __self.raffle_ticket_requests[j].tickets * __self.raffle_ticket_cost;
  1030. if (rows[i].points >= money) {
  1031. for (var k = 1; k <= __self.raffle_ticket_requests[j].tickets; k++) {
  1032. __self.raffle_tickets.push(__self.raffle_ticket_requests[j].viewer);
  1033. }
  1034. if (i !== rows.length - 1) {
  1035. sql += 'UPDATE viewers ';
  1036. sql += 'SET points = points - ' + money + ' ';
  1037. sql += 'WHERE user = \'' + rows[i].user + '\'; ';
  1038. } else {
  1039. sql += 'UPDATE viewers ';
  1040. sql += 'SET points = points - ' + money + ' ';
  1041. sql += 'WHERE user = \'' + rows[i].user + '\'';
  1042. }
  1043. }
  1044. break;
  1045. }
  1046. }
  1047. }
  1048. // randomize array before selecting a random winner
  1049. __self.raffle_tickets.sort(function () {return 0.5 - Math.random();});
  1050. // select random ticket from array
  1051. var winner = utils.selectRandomArrayItem(__self.raffle_tickets);
  1052. // count winner's tickets
  1053. var winning_ticket_amount;
  1054. for (var i = 0; i < __self.raffle_ticket_requests.length; i++) {
  1055. if (__self.raffle_ticket_requests[i].viewer === winner) {
  1056. winning_ticket_amount = __self.raffle_ticket_requests[i].tickets;
  1057. break;
  1058. }
  1059. }
  1060. // output winner to chat
  1061. __self.irc.emit('message', {message:__self.pre_text + 'Raffle closed, ' + __self.raffle_tickets.length + ' tickets purchased!'});
  1062. __self.irc.emit('message', {message:'+ Winner: ' + winner + ' (' + winning_ticket_amount + ' tickets purchased)'});
  1063. // remove one ticket from raffle bowl
  1064. if (__self.raffle_tickets.indexOf(winner) >= 0 ) {
  1065. __self.raffle_tickets.splice(__self.raffle_tickets.indexOf(winner), 1);
  1066. }
  1067. // execute query
  1068. __self.db.execute(sql, function () {});
  1069. });
  1070. } else {
  1071. // no tickets to pick from response
  1072. __self.irc.emit('message', {message:__self.pre_text + 'Raffle closed, no tickets to draw a winner'});
  1073. }
  1074. };
  1075. Currency.prototype.next_raffle_winner = function () {
  1076. var __self = this, empty_list = [];
  1077. // custom dialog when there are no more raffle tickets
  1078. empty_list.push('Hey, I just met you and this is crazy, but there\'s no more tickets, so start an new raffle maybe?');
  1079. empty_list.push('Are there more tickets? Well, to tell you the truth, in all this excitement I kind of lost track myself.');
  1080. empty_list.push('Heyyyyyy there\'s no more tickets, Op, op, op, op, Open Raffle Style.');
  1081. empty_list.push('Da tickets! Da tickets! Where are all da tickets, boss?');
  1082. if (!__self.raffle_status) {
  1083. // draw next ticket or prompt to open new raffle
  1084. if (__self.raffle_tickets.length > 0) {
  1085. // randomize array before selecting a random winner
  1086. __self.raffle_tickets.sort(function () {return 0.5 - Math.random();});
  1087. // select random ticket from array
  1088. var winner = utils.selectRandomArrayItem(__self.raffle_tickets);
  1089. // count next winner's tickets
  1090. var winning_ticket_amount;
  1091. for (var i = 0; i < __self.raffle_ticket_requests.length; i++) {
  1092. if (__self.raffle_ticket_requests[i].viewer === winner) {
  1093. winning_ticket_amount = __self.raffle_ticket_requests[i].tickets;
  1094. break;
  1095. }
  1096. }
  1097. // output winner to chat
  1098. __self.irc.emit('message', {message:__self.pre_text + 'Drawing next ticket'});
  1099. __self.irc.emit('message', {message:'+ Winner: ' + winner + ' (' + winning_ticket_amount + ' tickets purchased)'});
  1100. // remove one ticket from raffle bowl
  1101. if (__self.raffle_tickets.indexOf(winner) >= 0 ) {
  1102. __self.raffle_tickets.splice(__self.raffle_tickets.indexOf(winner), 1);
  1103. }
  1104. } else {
  1105. __self.irc.emit('message', {message:__self.pre_text + utils.selectRandomArrayItem(empty_list)});
  1106. }
  1107. }
  1108. };
  1109. /**
  1110. * ============================================
  1111. * BETTING SYSTEM
  1112. * --------------------------------------------
  1113. */
  1114. Currency.prototype.bets = function(status, data) {
  1115. var __self = this;
  1116. switch(status){
  1117. case true:
  1118. if (!__self.auction_status) {
  1119. if (!__self.raffle_status) {
  1120. if (!__self.bets_status && !__self.bets_payout) {
  1121. var wager_msg = '';
  1122. // open up bets
  1123. __self.bets_status = true;
  1124. __self.bets_payout = true;
  1125. // clear previous board / bets
  1126. __self.bets_board = [];
  1127. __self.bets_viewers = [];
  1128. __self.bets_total = 0;
  1129. // create new betting board
  1130. __self.bets_board = data.join().split(',').filter(function(n){return n}).slice(6).map(function(n){return {name: n, num: 0, total: 0};});
  1131. for (var i = 0; i < __self.bets_board.length; i++) {
  1132. __self.bets_board[i].idx = i;
  1133. }
  1134. // create chat message on how to place a bet
  1135. for (var i = 0; i < __self.bets_board.length; i++) {
  1136. if (i !== __self.bets_board.length - 1) {
  1137. wager_msg += '"!' + __self.bets_board[i].name + '" / ';
  1138. } else {
  1139. wager_msg += '"!' + __self.bets_board[i].name + '"';
  1140. }
  1141. }
  1142. // output to chat
  1143. __self.irc.emit('message', {message:__self.pre_text + 'Betting is now open'});
  1144. __self.irc.emit('message', {message:'+ Type ' + wager_msg + ' and the bet amount to enter'});
  1145. } else {
  1146. if (__self.bets_payout) {
  1147. // payout pending message
  1148. __self.irc.emit('message', {message:__self.pre_text + 'Unable to take new bets until previous have been paid out'});
  1149. } else {
  1150. // gambling is already open response
  1151. __self.irc.emit('message', {message:__self.pre_text + 'Betting already in progress'});
  1152. }
  1153. }
  1154. } else {
  1155. // raffle in progress
  1156. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open a raffle'});
  1157. }
  1158. } else {
  1159. // auction currently running
  1160. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can open an auction'});
  1161. }
  1162. break;
  1163. case false:
  1164. if (__self.bets_status && __self.bets_payout) {
  1165. // close out bets
  1166. __self.bets_status = false;
  1167. // deduct bets from viewers amounts
  1168. __self.bets_deduct_bets();
  1169. // output to chat
  1170. if (__self.bets_viewers.length > 0) {
  1171. __self.irc.emit('message', {message:__self.pre_text + 'Betting is now closed'});
  1172. for (var i = 0; i < __self.bets_board.length; i++) {
  1173. __self.irc.emit('message', {message:'+ ' + __self.bets_board[i].name + ' with ' + __self.bets_board[i].num + ' bets totaling ' + __self.bets_board[i].total});
  1174. }
  1175. } else {
  1176. __self.irc.emit('message', {message:__self.pre_text + 'Betting closed, no bets were placed'});
  1177. }
  1178. }
  1179. break;
  1180. case 'pool':
  1181. if (__self.bets_board.length > 0) {
  1182. __self.irc.emit('message', {message:__self.pre_text + 'Current betting pool is:'});
  1183. for (var i = 0; i < __self.bets_board.length; i++) {
  1184. __self.irc.emit('message', {message:'+ ' + __self.bets_board[i].name + ' with ' + __self.bets_board[i].num + ' bets totaling ' + __self.bets_board[i].total});
  1185. }
  1186. } else {
  1187. __self.irc.emit('message', {message:__self.pre_text + 'No current bet.'});
  1188. }
  1189. break;
  1190. case 'winner':
  1191. if (!__self.bets_status) {
  1192. if (__self.bets_payout) {
  1193. for (var i = 0; i < __self.bets_board.length; i++) {
  1194. if (data == __self.bets_board[i].name) {
  1195. __self.bets_award_winner(__self.bets_board[i]);
  1196. __self.irc.emit('message', {message:__self.pre_text + 'Betting payed out to ' + data});
  1197. }
  1198. }
  1199. // Clear the board
  1200. __self.bets_board = [];
  1201. __self.bets_viewers = [];
  1202. __self.bets_total = 0;
  1203. } else {
  1204. __self.irc.emit('message', {message:__self.pre_text + 'Betting has already payed out'});
  1205. }
  1206. } else {
  1207. __self.irc.emit('message', {message:__self.pre_text + 'Betting must be closed before you can have a winner'});
  1208. }
  1209. break;
  1210. }
  1211. };
  1212. Currency.prototype.collect_bets = function (caller, bet, amount) {
  1213. var __self = this, has_bet = false;
  1214. function do_work() {
  1215. var multi_response = [];
  1216. for (var i = 0; i < __self.bets_board.length; i++) {
  1217. multi_response.push([]);
  1218. }
  1219. if (__self.bets_flood.length > 0) {// send flood requests
  1220. var bet;
  1221. // setup bet response
  1222. for (var i = 0; i < __self.bets_flood.length; i++) {
  1223. bet = __self.bets_viewers[__self.bets_flood[i]];
  1224. multi_response[bet.bet].push(bet.viewer + ' (' + bet.amount + ')');
  1225. }
  1226. for (var i = 0; i < __self.bets_board.length; i++) {
  1227. if (multi_response[i].length > 0) {
  1228. __self.irc.emit('message', {message:'New bets for ' + __self.bets_board[i].name + ': ' + multi_response[i].join(', '), timer: 1});
  1229. }
  1230. }
  1231. }
  1232. // clear flood requests
  1233. __self.bets_flood = [];
  1234. }
  1235. // Bound amount by positive currency
  1236. __self.query_coins(caller, function (rows) {
  1237. for (var i = 0; i < rows.length; i++) {
  1238. amount = Math.min(amount, rows[i].points);
  1239. }
  1240. if (__self.bets_viewers.length > 0) {
  1241. for (var i = 0; i < __self.bets_viewers.length; i++) {
  1242. if (__self.bets_viewers[i].viewer === caller) {
  1243. has_bet = true;
  1244. __self.bets_board[__self.bets_viewers[i].bet].num -= 1;
  1245. __self.bets_board[__self.bets_viewers[i].bet].total -= __self.bets_viewers[i].amount;
  1246. __self.bets_viewers[i].bet = bet.idx;
  1247. __self.bets_viewers[i].amount = amount;
  1248. if (amount >= 1) {
  1249. __self.bets_board[bet.idx].num += 1;
  1250. __self.bets_board[bet.idx].total += amount;
  1251. // add flood users to array
  1252. if (__self.bets_flood.indexOf(i) < 0) {
  1253. __self.bets_flood.push(i);
  1254. }
  1255. // remove from flood if nullified bet
  1256. } else if (__self.bets_flood.indexOf(i) > 0) {
  1257. __self.bets_flood.splice(__self.bets_flood.indexOf(i), 1);
  1258. }
  1259. break;
  1260. }
  1261. }
  1262. if (!has_bet && amount >= 1 && amount !== 0) {
  1263. __self.bets_viewers.push({viewer: caller, bet: bet.idx, amount: amount});
  1264. __self.bets_board[bet.idx].num += 1;
  1265. __self.bets_board[bet.idx].total += amount;
  1266. __self.bets_flood.push(__self.bets_viewers.length - 1);
  1267. }
  1268. } else {
  1269. if (amount >= 1 && amount !== 0) {
  1270. __self.bets_viewers.push({viewer: caller, bet: bet.idx, amount: amount});
  1271. __self.bets_board[bet.idx].num += 1;
  1272. __self.bets_board[bet.idx].total += amount;
  1273. __self.bets_flood.push(__self.bets_viewers.length - 1);
  1274. }
  1275. }
  1276. console.log(__self.bets_viewers);
  1277. // check if flood has a set amount of requests and output
  1278. // if not, set the output timer
  1279. if (__self.bets_flood.length === __self.max_requests) {
  1280. do_work();
  1281. } else {
  1282. if (__self.bets_response_reset) {
  1283. __self.bets_response = setTimeout(function () {do_work();}, __self.bets_response_timer);
  1284. } else {
  1285. setTimeout(function () {do_work();}, __self.bets_response_timer);
  1286. }
  1287. }
  1288. });
  1289. };
  1290. Currency.prototype.bets_deduct_bets = function () {
  1291. var __self = this;
  1292. if (__self.bets_viewers.length > 0) {
  1293. // Don't trust the on-the-fly numbers
  1294. for (var i = 0; i < __self.bets_board.length; i++) {
  1295. __self.bets_board[i].num = 0;
  1296. __self.bets_board[i].total = 0;
  1297. }
  1298. __self.bets_total = 0;
  1299. // Remove the points and add them to an escrow
  1300. for (var i = 0; i < __self.bets_viewers.length; i++) {
  1301. if (__self.bets_viewers[i].amount > 0) {
  1302. sql = '';
  1303. sql += 'UPDATE viewers ';
  1304. sql += 'SET points = points - ' + __self.bets_viewers[i].amount + ' ';
  1305. sql += 'WHERE user = \'' + __self.bets_viewers[i].viewer + '\'';
  1306. __self.db.execute(sql, function() {});
  1307. __self.bets_board[__self.bets_viewers[i].bet].num += 1;
  1308. __self.bets_board[__self.bets_viewers[i].bet].total += __self.bets_viewers[i].amount;
  1309. __self.bets_total += __self.bets_viewers[i].amount;
  1310. }
  1311. }
  1312. }
  1313. };
  1314. Currency.prototype.bets_award_winner = function (winner) {
  1315. var __self = this;
  1316. if (__self.bets_payout) {
  1317. // set payout to complete
  1318. __self.bets_payout = false;
  1319. }
  1320. // Remove the points and add them to an escrow
  1321. if (__self.bets_viewers.length > 0) {
  1322. for (var i = 0; i < __self.bets_viewers.length; i++) {
  1323. if (__self.bets_viewers[i].bet == winner.idx) {
  1324. sql = '';
  1325. sql += 'UPDATE viewers ';
  1326. sql += 'SET points = points + ' + Math.ceil(__self.bets_total * __self.bets_viewers[i].amount / __self.bets_board[__self.bets_viewers[i].bet].total) + ' ';
  1327. sql += 'WHERE user = \'' + __self.bets_viewers[i].viewer + '\'';
  1328. __self.db.execute(sql, function() {});
  1329. }
  1330. }
  1331. }
  1332. };
  1333. module.exports = function (irc, db, options) {
  1334. return new Currency(irc, db, options);
  1335. };