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.

1710 lines
69 KiB

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