Advertisement
Guest User

Untitled

a guest
Mar 4th, 2015
190
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.48 KB | None | 0 0
  1. var irc = require('irc')
  2. , winston = require('winston')
  3. , fs = require('fs')
  4. , yaml = require('js-yaml')
  5. , coin = require('node-cannacoin')
  6. , crypto = require('crypto')
  7. , request = require('request')
  8. , webadmin = require('../lib/webadmin/app')
  9. , async = require('async');
  10.  
  11. //Locks & lsat active for rain.
  12. var last_active = {};
  13. var locks = [];
  14.  
  15. // check if the config file exists
  16. if(!fs.existsSync('./config/config.yml')) {
  17. winston.error('Configuration file doesn\'t exist! Please read the README.md file first.');
  18. process.exit(1);
  19. }
  20.  
  21. // load settings
  22. var settings = yaml.load(fs.readFileSync('./config/config.yml', 'utf-8'));
  23.  
  24. // handle sigint
  25. process.on('exit', function() {
  26. winston.info('Exiting...');
  27. if(client != null) {
  28. client.disconnect('My master ordered me to leave.');
  29. }
  30. });
  31.  
  32. // load winston's cli defaults
  33. winston.cli();
  34.  
  35. // write logs to file
  36. if(settings.log.file) {
  37. winston.add(winston.transports.File, {
  38. filename: settings.log.file,
  39. level: 'info'
  40. });
  41. }
  42.  
  43. // connect to coin json-rpc
  44. winston.info('Connecting to ' + settings.coin.full_name + ' daemon...');
  45.  
  46. var coin = coin({
  47. host: settings.rpc.host,
  48. port: settings.rpc.port,
  49. user: settings.rpc.user,
  50. pass: settings.rpc.pass
  51. });
  52.  
  53. coin.getBalance(function(err, balance) {
  54. if(err) {
  55. winston.error('Could not connect to %s RPC API! ', settings.coin.full_name, err);
  56. // process.exit(1);
  57. return;
  58. }
  59.  
  60. var balance = typeof(balance) == 'object' ? balance.result : balance;
  61. winston.info('Connected to JSON RPC API. Current total balance is %d' + settings.coin.short_name, balance);
  62. })
  63.  
  64. // run webadmin
  65. if(settings.webadmin.enabled){
  66. winston.info('Running webadmin on port %d', settings.webadmin.port);
  67. webadmin.app(settings.webadmin.port, coin, settings, winston);
  68. }
  69.  
  70. // connect to the server
  71. winston.info('Connecting to ' + settings.connection.host + '...');
  72.  
  73. // create new client object
  74. var client = new irc.Client(settings.connection.host, settings.login.nickname, {
  75. port: settings.connection.port,
  76. secure: settings.connection.secure,
  77. channels: settings.channels,
  78. userName: settings.login.username,
  79. realName: settings.login.realname,
  80. debug: settings.connection.debug
  81. });
  82.  
  83. // gets user's login status
  84. irc.Client.prototype.isIdentified = function(nickname, callback) {
  85. // request login status
  86. this.say('NickServ', 'ACC ' + nickname);
  87.  
  88. // wait for response
  89. var listener = function(from, to, message) {
  90. // proceed only on NickServ's ACC response
  91. var regexp = new RegExp('^(\\S+) ACC (\\d)');
  92. if(from != undefined && from.toLowerCase() == 'nickserv' && regexp.test(message)) {
  93. var match = message.match(regexp);
  94. var user = match[1];
  95. var level = match[2];
  96.  
  97. // if the right response, call the callback and remove this listener
  98. if(user.toLowerCase() == nickname.toLowerCase()) {
  99. callback(level == 3);
  100. this.removeListener('notice', listener);
  101. }
  102. }
  103. }
  104.  
  105. this.addListener('notice', listener);
  106. }
  107.  
  108. irc.Client.prototype.getNames = function(channel, callback) {
  109. client.send('NAMES', channel);
  110. var listener = function(nicks) {
  111. var names = [];
  112. for(name in nicks) {
  113. names.push(name);
  114. }
  115. callback(names);
  116. this.removeListener('names' + channel, listener);
  117. }
  118.  
  119. this.addListener('names' + channel, listener);
  120. }
  121.  
  122. // gets a empty coin address
  123. irc.Client.prototype.getAddress = function(nickname, callback) {
  124. winston.debug('Requesting address for %s', nickname);
  125. coin.send('getaccountaddress', nickname.toLowerCase(), function(err, address) {
  126. if(err) {
  127. winston.error('Something went wrong while getting address. ' + err);
  128. callback(err);
  129.  
  130. return false;
  131. }
  132.  
  133. callback(false, address);
  134. });
  135. }
  136.  
  137. String.prototype.expand = function(values) {
  138. var global = {
  139. nick: client.nick
  140. }
  141. return this.replace(/%([a-zA-Z_]+)%/g, function(str, variable) {
  142. return typeof(values[variable]) == 'undefined' ?
  143. (typeof(settings.coin[variable]) == 'undefined' ?
  144. (typeof(global[variable]) == 'undefined' ?
  145. str : global[variable]) : settings.coin[variable]) : values[variable];
  146. });
  147. }
  148.  
  149. // 420 Watch
  150. date = new Date();
  151. if ( date.getHours() === 4 || date.getHours() === 16 && date.getMinutes() === 20 && date.getSeconds() === 0) {
  152. client.say(settings.broadcast.cannacoin, "The time is now 4:20 PST, light em up!");
  153. }
  154.  
  155. // basic client listeners
  156. client.addListener('registered', function(message) {
  157. winston.info('Connected to %s.', message.server);
  158. client.say('NickServ', 'IDENTIFY ' + settings.login.nickserv_password);
  159. });
  160.  
  161. client.addListener('error', function(message) {
  162. winston.error('Received an error from IRC network: ', message);
  163. });
  164.  
  165. client.addListener('message', function(from, channel, message) {
  166. last_active[from] = Date.now();
  167. var match = message.toLowerCase().match(/^(!?)(\S+)/);
  168. if(match == null) return;
  169. var prefix = match[1];
  170. var command = match[2];
  171. if(settings.commands[command]) {
  172. if(channel == client.nick && settings.commands[command].pm === false) return;
  173. if(channel != client.nick && (settings.commands[command].channel === false || prefix != '!')) return;
  174. }
  175. else {
  176. return;
  177. }
  178. // if pms, make sure to respond to pms instead to itself
  179. if(channel == client.nick) channel = from;
  180. // comands that don't require identifying
  181. if(command == 'help'.toLowerCase() || command == 'terms'.toLowerCase()){
  182. for(var i = 0; i < settings.messages[command].length; i++) {
  183. var message = settings.messages[command][i];
  184. client.say(channel, message.expand({}));
  185. }
  186.  
  187. return;
  188. }
  189. // if not that, message will be undefined for some reason
  190. // todo: find a fix for that
  191. var msg = message;
  192. client.isIdentified(from, function(status) {
  193. var message = msg;
  194. // check if the sending user is logged in (identified) with nickserv
  195. if(!status) {
  196. winston.info('%s tried to use command `%s`, but is not identified.', from, message);
  197. client.say(channel, settings.messages.not_identified.expand({name: from}));
  198. return;
  199. }
  200. switch(command) {
  201. case 'rain':
  202. var match = message.match(/^.?rain ([\d\.]+) ?(\d+)?/);
  203.  
  204. if(match == null || !match[1]) {
  205. client.say(channel, 'Usage: !rain <amount> [max people]');
  206. return;
  207. }
  208.  
  209. var amount = Number(match[1]);
  210. var max = Number(match[2]);
  211.  
  212. if(isNaN(amount)) {
  213. client.say(channel, settings.messages.invalid_amount.expand({name: from, amount: match[2]}));
  214. return;
  215. }
  216.  
  217. if(isNaN(max) || max < 1) {
  218. max = false;
  219. } else {
  220. max = Math.floor(max);
  221. }
  222.  
  223. //If user is locked, return (do not tip).
  224. if(locks.indexOf(from.toLowerCase()) > -1) {
  225. client.say(channel, settings.messages.locked.expand({name: from}));
  226. return;
  227. }
  228. // Enable lock
  229. locks.push(from.toLowerCase());
  230.  
  231.  
  232. coin.getBalance(settings.rpc.prefix + from.toLowerCase(), settings.coin.min_confirmations, function(err, balance) {
  233. if(err) {
  234. locks.splice(locks.indexOf(from.toLowerCase()),1);
  235. winston.error('Error in !rain command.', err);
  236. client.say(channel, settings.messages.error.expand({name: from}));
  237. return;
  238. }
  239. var balance = typeof(balance) == 'object' ? balance.result : balance;
  240.  
  241. if(balance >= amount) {
  242. client.getNames(channel, function(names) {
  243. // rain only on nicknames active within the last x seconds
  244. if(settings.commands.rain.rain_on_last_active) {
  245. for (var i = names.length - 1; i >= 0; i--) {
  246. if(!last_active.hasOwnProperty(names[i]) || last_active[names[i]] + settings.commands.rain.rain_on_last_active * 1000 < Date.now()) {
  247. names.splice(i, 1);
  248. }
  249. };
  250. }
  251. // remove tipper from the list
  252. names.splice(names.indexOf(from), 1);
  253.  
  254. // shuffle the array
  255. for(var j, x, i = names.length; i; j = Math.floor(Math.random() * i), x = names[--i], names[i] = names[j], names[j] = x);
  256.  
  257. max = max ? Math.min(max, names.length) : names.length;
  258. if(max == 0) return;
  259. var whole_channel = false;
  260. if(max == names.length) whole_channel = true;
  261. names = names.slice(0, max);
  262.  
  263. if(amount / max < settings.coin.min_rain) {
  264. locks.splice(locks.indexOf(from.toLowerCase()),1);
  265. client.say(channel, settings.messages.rain_too_small.expand({from: from, amount: amount, min_rain: settings.coin.min_rain * max}));
  266. return;
  267. }
  268.  
  269. for (var i = 0; i < names.length; i++) {
  270. coin.move(settings.rpc.prefix + from.toLowerCase(), settings.rpc.prefix + names[i].toLowerCase(), amount / max, function(err, reply) {
  271. if(i == names.length) locks.splice(locks.indexOf(from.toLowerCase()),1);
  272. if(err || !reply) {
  273. winston.error('Error in !rain command', err);
  274. return;
  275. }
  276. });
  277. }
  278. client.say(channel, settings.messages.rain.expand({name: from, amount: parseFloat((amount / max).toFixed(8)), list: (whole_channel && !settings.commands.rain.rain_on_last_active) ? 'the whole channel' : names.join(', ')}));
  279. });
  280. } else {
  281. locks.splice(locks.indexOf(from.toLowerCase()),1);
  282. client.say(channel, settings.messages.no_funds.expand({name: from, balance: balance, short: amount - balance, amount: amount}));
  283. }
  284. })
  285. break;
  286.  
  287. case 'flip':
  288. var match = message.match(/^.?flip ([\d\.]+)/);
  289.  
  290. if(match == null || !match[1]) {
  291. client.say(channel, 'Usage: !flip <amount>');
  292. return;
  293. }
  294.  
  295. var amount = Number(match[1]);
  296. var max = 1;
  297.  
  298. if(isNaN(amount)) {
  299. client.say(channel, settings.messages.invalid_amount.expand({name: from, amount: match[2]}));
  300. return;
  301. }
  302.  
  303. //If user is locked, return (do not tip).
  304. if(locks.indexOf(from.toLowerCase()) > -1) {
  305. client.say(channel, settings.messages.locked.expand({name: from}));
  306. return;
  307. }
  308. // Enable lock
  309. locks.push(from.toLowerCase());
  310.  
  311.  
  312. coin.getBalance(settings.rpc.prefix + from.toLowerCase(), settings.coin.min_confirmations, function(err, balance) {
  313. if(err) {
  314. locks.splice(locks.indexOf(from.toLowerCase()),1);
  315. winston.error('Error in !tip command.', err);
  316.  
  317. client.say(channel, settings.messages.error.expand({name: from}));
  318. return;
  319. }
  320. var balance = typeof(balance) == 'object' ? balance.result : balance;
  321.  
  322. if(balance >= amount) {
  323. client.getNames(channel, function(names) {
  324. // rain only on nicknames active within the last x seconds
  325. if(settings.commands.rain.rain_on_last_active) {
  326. for (var i = names.length - 1; i >= 0; i--) {
  327. if(!last_active.hasOwnProperty(names[i]) || last_active[names[i]] + settings.commands.rain.rain_on_last_active * 1000 < Date.now()) {
  328. names.splice(i, 1);
  329. }
  330. };
  331. }
  332. // remove tipper from the list
  333. names.splice(names.indexOf(from), 1);
  334. // shuffle the array
  335. for(var j, x, i = names.length; i; j = Math.floor(Math.random() * i), x = names[--i], names[i] = names[j], names[j] = x);
  336.  
  337. max = max ? Math.min(max, names.length) : names.length;
  338. if(max == 0) return;
  339. var whole_channel = false;
  340. if(max == names.length) whole_channel = true;
  341. names = names.slice(0, max);
  342.  
  343. if(amount / max < settings.coin.min_rain) {
  344. locks.splice(locks.indexOf(from.toLowerCase()),1);
  345. client.say(channel, settings.messages.rain_too_small.expand({from: from, amount: amount, min_rain: settings.coin.min_rain * max}));
  346. return;
  347. }
  348.  
  349. for (var i = 0; i < names.length; i++) {
  350. coin.move(settings.rpc.prefix + from.toLowerCase(), settings.rpc.prefix + names[i].toLowerCase(), amount / max, function(err, reply) {
  351. if(i == names.length) locks.splice(locks.indexOf(from.toLowerCase()),1);
  352. if(err || !reply) {
  353. winston.error('Error in !tip command', err);
  354. return;
  355. }
  356. });
  357. }
  358.  
  359. client.say(channel, settings.messages.rain.expand({name: from, amount: parseFloat((amount / max).toFixed(8)), list: (whole_channel && !settings.commands.rain.rain_on_last_active) ? 'the whole channel' : names.join(', ')}));
  360. });
  361. } else {
  362. client.say(channel, settings.messages.no_funds.expand({name: from, balance: balance, short: amount - balance, amount: amount}));
  363. }
  364. })
  365. break;
  366.  
  367. case 'tip':
  368. var match = message.match(/^.?tip (\S+) ([\d\.]+)/);
  369.  
  370. if(match == null || match.length < 3) {
  371. client.say(channel, 'Usage: !tip <nickname> <amount>')
  372. return;
  373. }
  374.  
  375. var to = match[1];
  376. var amount = Number(match[2]);
  377.  
  378. //If user is locked, return (do not tip).
  379. if(locks.indexOf(from.toLowerCase()) > -1) {
  380. client.say(channel, settings.messages.locked.expand({name: from}));
  381. return;
  382. }
  383. // Enable lock
  384. locks.push(from.toLowerCase());
  385.  
  386. if(isNaN(amount)) {
  387. locks.splice(locks.indexOf(from.toLowerCase()),1);
  388. client.say(channel, settings.messages.invalid_amount.expand({name: from, amount: match[2]}));
  389. return;
  390. }
  391.  
  392. if("irc_"+to.toLowerCase() == "irc_"+from.toLowerCase()) {
  393. //Reset user lock
  394. locks.splice(locks.indexOf(from.toLowerCase()),1);
  395. client.say(channel, settings.messages.tip_self.expand({name: from}));
  396. return;
  397. }
  398.  
  399. if(amount < settings.coin.min_tip) {
  400. //Reset lock
  401. locks.splice(locks.indexOf(from.toLowerCase()),1);
  402. client.say(channel, settings.messages.tip_too_small.expand({from: from, to: to, amount: amount}));
  403. return;
  404. }
  405.  
  406. // check balance with min. 5 confirmations
  407. coin.getBalance("irc_"+from.toLowerCase(), settings.coin.min_confirmations, function(err, balance) {
  408. if(err) {
  409. //Reset lock
  410. locks.splice(locks.indexOf(from.toLowerCase()),1);
  411. winston.error('Error in !tip command.', err);
  412. client.say(channel, settings.messages.error.expand({name: from}));
  413. return;
  414. }
  415.  
  416. var balance = typeof(balance) == 'object' ? balance.result : balance;
  417.  
  418. if(balance >= amount) {
  419. client.isIdentified(to, function(status) {
  420. var message = msg;
  421. // check if the sending user is logged in (identified) with nickserv
  422. if(!status) {
  423. locks.splice(locks.indexOf(from.toLowerCase()),1);
  424. client.say(channel, settings.messages.tip_not_identified.expand({name: to}));
  425. return;
  426. }
  427. coin.send('move', "irc_"+from.toLowerCase(), "irc_"+to.toLowerCase(), amount, function(err, reply) {
  428. if(err || !reply) {
  429. locks.splice(locks.indexOf(from.toLowerCase()),1);
  430. winston.error('Error in !tip command', err);
  431. client.say(channel, settings.messages.error.expand({name: from}));
  432. return;
  433. }
  434. locks.splice(locks.indexOf(from.toLowerCase()),1);
  435. client.say(channel, settings.messages.tipped.expand({from: from, to: to, amount: amount}));
  436. });
  437. });
  438. } else {
  439. locks.splice(locks.indexOf(from.toLowerCase()),1);
  440. client.say(channel, settings.messages.no_funds.expand({name: from, balance: balance, short: amount - balance, amount: amount}));
  441. }
  442. });
  443. break;
  444.  
  445. case 'withdraw':
  446. var match = message.match(/^.?withdraw (\S+)$/);
  447. if(match == null) {
  448. client.say(channel, 'Usage: !withdraw <' + settings.coin.full_name + ' address>');
  449. return;
  450. }
  451. var address = match[1];
  452.  
  453. //If user is locked, return (do not tip).
  454. if(locks.indexOf(from.toLowerCase()) > -1) {
  455. client.say(channel, settings.messages.locked.expand({name: from}));
  456. return;
  457. }
  458. // Enable lock
  459. locks.push(from.toLowerCase());
  460.  
  461. coin.validateAddress(address, function(err, reply) {
  462. if(err) {
  463. locks.splice(locks.indexOf(from.toLowerCase()),1);
  464. winston.error('Error in !withdraw command', err);
  465. client.say(channel, settings.messages.error.expand({name: from}));
  466. return;
  467. }
  468.  
  469. if(reply.isvalid) {
  470. coin.getBalance(settings.rpc.prefix + from.toLowerCase(), settings.coin.min_confirmations, function(err, balance) {
  471. if(err) {
  472. locks.splice(locks.indexOf(from.toLowerCase()),1);
  473. winston.error('Error in !withdraw command', err);
  474. client.say(channel, settings.messages.error.expand({name: from}));
  475. return;
  476. }
  477. var balance = typeof(balance) == 'object' ? balance.result : balance;
  478.  
  479. if(balance < settings.coin.min_withdraw) {
  480. locks.splice(locks.indexOf(from.toLowerCase()),1);
  481. winston.warn('%s tried to withdraw %d, but min is set to %d', from, balance, settings.coin.min_withdraw);
  482. client.say(channel, settings.messages.withdraw_too_small.expand({name: from, balance: balance}));
  483. return;
  484. }
  485.  
  486. coin.sendFrom(settings.rpc.prefix + from.toLowerCase(), address, balance - settings.coin.withdrawal_fee, function(err, reply) {
  487. if(err) {
  488. locks.splice(locks.indexOf(from.toLowerCase()),1);
  489. winston.error('Error in !withdraw command', err);
  490. client.say(channel, settings.messages.error.expand({name: from}));
  491. return;
  492. }
  493. locks.splice(locks.indexOf(from.toLowerCase()),1);
  494. var values = {name: from, address: address, balance: balance, amount: balance - settings.coin.withdrawal_fee, transaction: reply}
  495. for(var i = 0; i < settings.messages.withdraw_success.length; i++) {
  496. var msg = settings.messages.withdraw_success[i];
  497. client.say(channel, msg.expand(values));
  498. };
  499. });
  500. });
  501. } else {
  502. winston.warn('%s tried to withdraw to an invalid address', from);
  503. client.say(channel, settings.messages.invalid_address.expand({address: address, name: from}));
  504. }
  505. });
  506. break;
  507. }
  508. });
  509. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement