Advertisement
Joker95

Untitled

Feb 9th, 2018
255
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 25.29 KB | None | 0 0
  1. var fs = require('fs');
  2. var crypto = require('crypto');
  3. var console = process.console;
  4. var config = require('./config.js');
  5. var getSteamAPIKey = require('steam-web-api-key');
  6. var redisClient, io, requestify;
  7. var requestify = require('requestify');
  8.  
  9. module.exports.init = function(redis, ioSocket, requestifyCore) {
  10. io = ioSocket;
  11. redisClient = redis.createClient();
  12. requestify = requestifyCore;
  13. }
  14.  
  15. var hash = require('crypto').createHash('sha1');
  16. hash.update(Math.random().toString());
  17. hash = hash.digest('hex');
  18. var device_id = 'android:' + hash;
  19.  
  20. function log(log) {console.log('[BOTS] ' + log);}
  21.  
  22. requestify.post('http://' + config.domain + '/api/getBots')
  23. .then((response) => {
  24. response = JSON.parse(response.body);
  25. if(!response.success) {
  26. log('Не удалось найти бота!');
  27. return;
  28. }
  29. getBots(response.bots);
  30. }, (response) => {
  31. log(JSON.stringify(response.body));
  32. });
  33.  
  34. function getBots(bots) {
  35.  
  36. for(var i = 0; i < bots.length; i++) startBot(bots[i]);
  37.  
  38. }
  39.  
  40. function startBot(data) {
  41. function botLog(log) {
  42. console.tag('ROULETTE', logTime()).log(log);
  43. }
  44. var logOnOptions = {
  45. account_name : data.username,
  46. password : data.password
  47. }
  48.  
  49. var g_Steamid = data.steamid64;
  50. var g_Device_ID = device_id;
  51. var g_Indentity_Secret = data.identity;
  52. var g_Shared_Secret = data.shared;
  53.  
  54. function getSHA1(bytes) {
  55. var shasum = crypto.createHash('sha1');
  56. shasum.end(bytes);
  57. return shasum.read();
  58. }
  59.  
  60. var Steam = require('steam');
  61. var steam = new Steam.SteamClient();
  62. var SteamTradeOffers = require('steam-tradeoffers');
  63. var offers = new SteamTradeOffers();
  64. var steamUser = new Steam.SteamUser(steam);
  65. var SteamTotp = require('steam-totp');
  66. var steamFriends = new Steam.SteamFriends(steam);
  67. var SteamWebLogOn = require('steam-weblogon');
  68. var steamWebLogOn = new SteamWebLogOn(steam, steamUser);
  69. var SteamConf = require('steamcommunity-mobile-confirmations');
  70. var steamConf;
  71. var confirmTimer;
  72. steam.connect();
  73.  
  74. function GetEscrowCode(){
  75. botLog('Код авторизиции : '+SteamTotp.generateAuthCode(data.shared));
  76. setTimeout(function(){
  77. GetEscrowCode()
  78. }, 30000);
  79. }
  80. GetEscrowCode();
  81. logOnOptions['two_factor_code'] = SteamTotp.generateAuthCode(g_Shared_Secret);
  82. steam.on('connected', function() {
  83. steamUser.logOn(logOnOptions);
  84. });
  85.  
  86. steam.on('debug', botLog);
  87.  
  88. steam.on('logOnResponse', function(result) {
  89. if (result.eresult === Steam.EResult.OK) {
  90. botLog('Авторизовались!');
  91. reWebLogOn(steam);
  92. steamFriends.setPersonaState(Steam.EPersonaState.Online);
  93. } else {
  94. botLog('Ошибка при авторизиции - ' + result.eresult);
  95. }
  96. });
  97.  
  98.  
  99. function logTime() {
  100.  
  101. var date = new Date();
  102. var hour = date.getHours();
  103. var min = date.getMinutes();
  104. var sec = date.getSeconds();
  105.  
  106. var year = date.getFullYear();
  107. var month = date.getMonth() + 1;
  108. var day = date.getDate();
  109.  
  110. hour = (hour < 10 ? "0" : "") + hour;
  111. min = (min < 10 ? "0" : "") + min;
  112. sec = (sec < 10 ? "0" : "") + sec;
  113. month = (month < 10 ? "0" : "") + month;
  114. day = (day < 10 ? "0" : "") + day;
  115.  
  116. return hour + ":" + min + ":" + sec;
  117. }
  118.  
  119.  
  120. var checkingOffers = [],
  121. WebSession = false,
  122. globalSession;
  123.  
  124. function reWebLogOn(steam, callback) {
  125. steamWebLogOn.webLogOn(function(webSessionID, newCookie){
  126. botLog('Подключили cookie! - ' + newCookie);
  127. steamConf = new SteamConf(
  128. {
  129. steamid: g_Steamid,
  130. identity_secret: g_Indentity_Secret,
  131. device_id: g_Device_ID,
  132. webCookie: newCookie,
  133. });
  134. getSteamAPIKey({
  135. sessionID: webSessionID,
  136. webCookie: newCookie
  137. }, function(err, APIKey) {
  138. offers.setup({
  139. sessionID: webSessionID,
  140. webCookie: newCookie,
  141. APIKey: APIKey
  142. }, function(err){
  143. WebSession = true;
  144. globalSession = webSessionID;
  145. redisClient.lrange(redisChannels.tradeoffersList, 0, -1, function(err, offers){
  146. offers.forEach(function(offer) {
  147. checkingOffers.push(offer);
  148. });
  149. handleOffers();
  150. });
  151. botLog('Настройка Офферов!');
  152. });
  153. botLog('Создал сессию! - '+ webSessionID);
  154. });
  155.  
  156. });
  157. }
  158.  
  159. function confirmTrade(){
  160. steamConf.FetchConfirmations((function (err, confirmations) {
  161. if (err) {
  162. botLog('Ошибка подтверждения оффера: ' + err);
  163. return;
  164. }
  165. botLog('Трейдов в ожидании:' + confirmations.length);
  166. if (!confirmations.length) {
  167. return;
  168. }
  169. steamConf.AcceptConfirmation(confirmations[0], (function (err, result) {
  170. if (err) {
  171. botLog('Ошибка подтверждения оффера: ' + err);
  172. return;
  173. }
  174. botLog('Result:' + result);
  175. }).bind(this));
  176. }).bind(this));
  177.  
  178.  
  179.  
  180. }
  181.  
  182.  
  183. const redisChannels = {
  184. checkItemsList: 'checkItems.list',
  185. checkList: 'check.list',
  186. checkedList: 'checked.list',
  187. betsList: 'bets.list',
  188. sendOffersList: 'send.offers.list',
  189. tradeoffersList: 'tradeoffers.list',
  190. declineList: 'decline.list',
  191. usersQueue: 'usersQueue.list'
  192. }
  193.  
  194.  
  195. steamUser.on('updateMachineAuth', function(sentry, callback) {
  196. fs.writeFileSync('sentry', sentry.bytes);
  197. callback({ sha_file: getSHA1(sentry.bytes) });
  198. });
  199.  
  200.  
  201.  
  202.  
  203.  
  204. function handleOffers() {
  205. offers.getOffers({
  206. get_received_offers: 1,
  207. active_only: 1
  208. }, function(error, body) {
  209. if (
  210. body
  211. && body.response
  212. && body.response.trade_offers_received
  213. ) {
  214. body.response.trade_offers_received.forEach(function(offer) {
  215. if (offer.trade_offer_state == 2) {
  216. if(is_checkingOfferExists(offer.tradeofferid)) return;
  217.  
  218. if(offer.items_to_give != null && config.admins.indexOf(offer.steamid_other) != -1) {
  219. botLog('Оффер #' + offer.tradeofferid + ' От: Admin ' + offer.steamid_other);
  220. offers.acceptOffer({tradeOfferId: offer.tradeofferid});
  221. confirmTrade();
  222. return;
  223. }
  224.  
  225. offers.getTradeHoldDuration({tradeOfferId : offer.tradeofferid}, function(err, response)
  226. {
  227. botLog(offer.steamid_other);
  228. botLog(offer.accessToken);
  229.  
  230. if (response && response.their != 0)
  231. {
  232. botLog('Hold TradeOfferID: '+offer.tradeofferid+' days: '+response.their);
  233.  
  234.  
  235. redisClient.multi([
  236. ['rpush', redisChannels.declineList, offer.tradeofferid],
  237. ['lrem', redisChannels.checkItemsList, 0, offer],
  238. ['lrem', redisChannels.checkedList, 0, offer]
  239. ])
  240. .exec(function (err, replies) {
  241. });
  242.  
  243.  
  244. requestify.post('http://'+config.domain+'/api/haventescrow', {secretKey: config.secretKey , steamid: offer.steamid_other});
  245.  
  246. return;
  247. }
  248.  
  249. });
  250. if(offer.items_to_give != null) {
  251. offers.declineOffer({tradeOfferId: offer.tradeofferid});
  252. return;
  253. }
  254. if (offer.items_to_receive != null && offer.items_to_give == null) {
  255. checkingOffers.push(offer.tradeofferid);
  256. botLog('Оффер #' + offer.tradeofferid + ' От: ' + offer.steamid_other);
  257. redisClient.multi([
  258. ['rpush', redisChannels.tradeoffersList, offer.tradeofferid],
  259. ['rpush', redisChannels.checkItemsList, JSON.stringify(offer)],
  260. ['rpush', redisChannels.usersQueue, offer.steamid_other]
  261. ]).exec(function(){
  262. redisClient.lrange(redisChannels.usersQueue, 0, -1, function(err, queues) {
  263. io.sockets.emit('queue', queues);
  264. });
  265. });
  266. return;
  267. }
  268. }
  269. });
  270. }
  271. });
  272. }
  273.  
  274.  
  275. steamUser.on('tradeOffers', function(number) {
  276. if (number > 0) {
  277. handleOffers();
  278. }
  279. });
  280.  
  281.  
  282. var parseOffer = function(offer, offerJson){
  283. offers.loadPartnerInventory({partnerSteamId: offer.steamid_other, appId: 730, contextId: 2, tradeOfferId: offer.tradeofferid, language: "russian"}, function(err, hitems) {
  284. if (err) {
  285. redisClient.multi([
  286. ['rpush', redisChannels.declineList, offer.tradeofferid],
  287. ['lrem', redisChannels.checkItemsList, 0, offerJson],
  288. ['lrem', redisChannels.usersQueue, 1, offer.steamid_other]
  289. ])
  290. .exec(function (err, replies) {
  291. parseItemsProcceed = false;
  292. return;
  293. });
  294. return;
  295. }
  296. var items = offer.items_to_receive;
  297. var items_to_check = [], num = 0;
  298. for (var i = 0; i < items.length; i++) {
  299. for (var j = 0; j < hitems.length; j++) {
  300. if (items[i].assetid == hitems[j].id) {
  301. items_to_check[num] = {
  302. appid:hitems[j].appid,
  303. name:hitems[j].market_name,
  304. market_hash_name:hitems[j].market_hash_name,
  305. classid:hitems[j].classid
  306. };
  307. var type = hitems[j].type;
  308. var rarity = '';
  309. var arr = type.split(',');
  310. if (arr.length == 2) type = arr[1].trim();
  311. if (arr.length == 3) type = arr[2].trim();
  312. if (arr.length && arr[0] == 'Нож') type = '★';
  313. switch (type) {
  314. case 'Армейское качество': rarity = 'milspec'; break;
  315. case 'Запрещенное': rarity = 'restricted'; break;
  316. case 'Засекреченное': rarity = 'classified'; break;
  317. case 'Тайное': rarity = 'covert'; break;
  318. case 'Ширпотреб': rarity = 'common'; break;
  319. case 'Промышленное качество': rarity = 'common'; break;
  320. case '★': rarity = 'rare'; break;
  321. }
  322. items_to_check[num].rarity = rarity;
  323. num++;
  324. break;
  325. }
  326. }
  327. }
  328. var value = {
  329. offerid: offer.tradeofferid,
  330. accountid: offer.steamid_other,
  331. items: JSON.stringify(items_to_check)
  332. };
  333.  
  334. console.tag('ROULETTE','OFFER:' + value.offerid).log(value);
  335.  
  336. redisClient.multi([
  337. ['rpush', redisChannels.checkList, JSON.stringify(value)],
  338. ['lrem', redisChannels.checkItemsList, 0, offerJson]
  339. ])
  340. .exec(function (err, replies) {
  341. parseItemsProcceed = false;
  342. });
  343.  
  344. });
  345. }
  346.  
  347. var checkOfferPrice = function () {
  348. requestify.get('http://' + config.domain + '/api/checkOffer', {
  349. secretKey: config.secretKey
  350. })
  351. .then(function (response) {
  352. var answer = JSON.parse(response.body);
  353.  
  354. if (answer.success) {
  355. checkProcceed = false;
  356. }
  357. }, function (response) {
  358. console.tag('SteamBot', logTime()).error('Что-то не так с проверкой офферов.');
  359. setTimeout(function () {
  360. checkOfferPrice()
  361. }, 1000);
  362. });
  363.  
  364. }
  365.  
  366. var checkNewBet = function () {
  367. requestify.get('http://' + config.domain + '/api/newBet', {
  368. secretKey: config.secretKey
  369. })
  370. .then(function (response) {
  371. var answer = JSON.parse(response.body);
  372. if (answer.success) {
  373. betsProcceed = false;
  374. }
  375. }, function (response) {
  376. console.tag('SteamBot', logTime()).error('Что-то не так с отправлением новой ставки.');
  377. setTimeout(function () {
  378. checkNewBet()
  379. }, 1000);
  380. });
  381. }
  382.  
  383. var checkArrGlobal = [];
  384.  
  385. var sendTradeOffer = function(appId, partnerSteamId, accessToken, sendItems, message, game, offerJson){
  386. try {
  387. offers.loadMyInventory({
  388. appId: appId,
  389. contextId: 2
  390. }, function (err, items) {
  391. if(err) {
  392. botLog(err);
  393. sendProcceed = false;
  394. return;
  395. }
  396. var itemsFromMe = [],
  397. checkArr = [],
  398. num = 0;
  399. var i = 0;
  400. for (var i = 0; i < sendItems.length; i++) {
  401. for (var j = 0; j < items.length; j++) {
  402. if (items[j].tradable && (items[j].classid == sendItems[i])) {
  403. if ((checkArr.indexOf(items[j].id) == -1) && (checkArrGlobal.indexOf(items[j].id) == -1)) {
  404. checkArr[i] = items[j].id;
  405. itemsFromMe[num] = {
  406. appid: 730,
  407. contextid: 2,
  408. amount: items[j].amount,
  409. assetid: items[j].id
  410. };
  411. num++;
  412. break;
  413. }
  414. }
  415. }
  416. }
  417. if (num > 0) {
  418. offers.makeOffer({
  419. partnerSteamId: partnerSteamId,
  420. accessToken: accessToken,
  421. itemsFromMe: itemsFromMe,
  422. itemsFromThem: [],
  423. message: 'Поздравляем с победой на сайте '+config.nameSite+' > В игре #' + game
  424. }, function (err, response) {
  425. if (err) {
  426. console.tag('SteamBot', 'SendPrize', logTime()).error('Ошибка отправления оффера:' + err.message);
  427. getErrorCode(err.message, function(errCode) {
  428. if(errCode == 2 || errCode == 3 || errCode == 6 || errCode == 15 || errCode == 16 || errCode == 25 || errCode == 50 || err.message.indexOf('was an error sending your trade offer. Please try again later.')) { // if there is an error in submitting the winning re-send it
  429. redisClient.lrem(redisChannels.sendOffersList, 0, offerJson, function (err, data) {
  430. setPrizeStatus(game, 2);
  431. CheckGameForSend(game, 1);
  432. setTimeout(function () {
  433. CheckGameForSend(game, 0);
  434. setPrizeStatus(game, 1);
  435. console.tag('SteamBot', 'SendPrize', logTime()).log('Прошло 10 минут, останавливем переотправку, и ставим статус раунда - 1.');
  436. }, 60000);
  437. sendProcceed = false;
  438. });
  439. sendProcceed = false;
  440. }
  441. sendProcceed = false;
  442. });
  443. return;
  444. }
  445. checkArrGlobal = checkArrGlobal.concat(checkArr);
  446. botLog(checkArrGlobal);
  447. botLog(checkArr);
  448. redisClient.lrem(redisChannels.sendOffersList, 0, offerJson, function(err, data){
  449. setPrizeStatus(game, 1);
  450. CheckGameForSend(game, 0);
  451. sendProcceed = false;
  452. });
  453. botLog('Оффер #' + response.tradeofferid +' отправлен!');
  454. confirmTrade();
  455. });
  456. }else{
  457. botLog('Нету предметов!');
  458. redisClient.lrem(redisChannels.sendOffersList, 0, offerJson, function(err, data){
  459. setPrizeStatus(game, 1);
  460. CheckGameForSend(game, 0);
  461. sendProcceed = false;
  462. });
  463. }
  464. });
  465.  
  466. }catch(ex){
  467. console.tag('SteamBot', logTime()).error('Error to send the bet');
  468. setPrizeStatus(game, 2);
  469. CheckGameForSend(game, 1);
  470. sendProcceed = false;
  471. }
  472. };
  473.  
  474.  
  475.  
  476. var setPrizeStatus = function(game, status){
  477. requestify.post('http://'+config.domain+'/api/setPrizeStatus', {
  478. secretKey: config.secretKey,
  479. game: game,
  480. status: status
  481. })
  482. .then(function(response) {
  483.  
  484. },function(response){
  485. botLog('Что-то не так с установкой prize status.');
  486. setTimeout(function(){setPrizeStatus()}, 1000);
  487. });
  488. }
  489.  
  490. var CheckGameForSend = function(game, status){
  491. requestify.post('http://'+config.domain+'/api/CheckGameForSend', {
  492. secretKey: config.secretKey,
  493. game: game,
  494. status: status
  495. })
  496. .then(function(response) {
  497.  
  498. },function(response){
  499. botLog('Что-то не так с установкой CheckGameForSend.');
  500. setTimeout(function(){CheckGameForSend()}, 1000);
  501. });
  502. }
  503.  
  504. var is_checkingOfferExists = function(tradeofferid){
  505. for(var i = 0, len = checkingOffers.length; i<len; ++i ){
  506. var offer = checkingOffers[i];
  507. if(offer == tradeofferid){
  508. return true;
  509. break;
  510. }
  511. }
  512. return false;
  513. }
  514.  
  515. var checkedOffersProcceed = function(offerJson){
  516. var offer = JSON.parse(offerJson);
  517. if (offer.success) {
  518. botLog('Принимаем: #' + offer.offerid);
  519. offers.acceptOffer({tradeOfferId: offer.offerid}, function (err, body) {
  520. if (!err) {
  521. redisClient.multi([
  522. ["lrem", redisChannels.tradeoffersList, 0, offer.offerid],
  523. ["lrem", redisChannels.usersQueue, 1, offer.steamid64],
  524. ["rpush", redisChannels.betsList, offerJson],
  525. ["lrem", redisChannels.checkedList, 0, offerJson]
  526. ])
  527. .exec(function (err, replies) {
  528. redisClient.lrange(redisChannels.usersQueue, 0, -1, function(err, queues) {
  529. io.sockets.emit('queue', queues);
  530. botLog("Новый депозит принят!");
  531. checkedProcceed = false;
  532. });
  533. });
  534.  
  535. } else {
  536. botLog('Ошибка. Не могу принять оффер #' + offer.offerid + ' Log: ' + err)
  537.  
  538. offers.getOffer({tradeOfferId: offer.offerid}, function (err, body){
  539. if(body && body.response && body.response.offer){
  540. var offerCheck = body.response.offer;
  541. if(offerCheck.trade_offer_state == 2) {
  542. checkedProcceed = false;
  543. return;
  544. }
  545. if(offerCheck.trade_offer_state == 3){
  546. redisClient.multi([
  547. ["lrem", redisChannels.tradeoffersList, 0, offer.offerid],
  548. ["lrem", redisChannels.usersQueue, 1, offer.steamid64],
  549. ["rpush", redisChannels.betsList, offerJson],
  550. ["lrem", redisChannels.checkedList, 0, offerJson]
  551. ])
  552. .exec(function (err, replies) {
  553. redisClient.lrange(redisChannels.usersQueue, 0, -1, function(err, queues) {
  554. io.sockets.emit('queue', queues);
  555. checkedProcceed = false;
  556. });
  557. });
  558. }else{
  559. redisClient.multi([
  560. ["lrem", redisChannels.tradeoffersList, 0, offer.offerid],
  561. ["lrem", redisChannels.usersQueue, 1, offer.steamid64],
  562. ["lrem", redisChannels.checkedList, 0, offerJson]
  563. ])
  564. .exec(function (err, replies) {
  565. redisClient.lrange(redisChannels.usersQueue, 0, -1, function(err, queues) {
  566. io.sockets.emit('queue', queues);
  567. checkedProcceed = false;
  568. });
  569. });
  570. }
  571. }
  572. })
  573. }
  574. });
  575. }
  576. }
  577.  
  578. var declineOffersProcceed = function(offerid){
  579. botLog('Отменяем: #' + offerid);
  580. offers.declineOffer({tradeOfferId: offerid}, function (err, body) {
  581. if (!err) {
  582. botLog('Оффер #' + offerid + ' Отменен!');
  583. redisClient.lrem(redisChannels.declineList, 0, offerid);
  584. declineProcceed = false;
  585. } else {
  586. botLog('Ошибка. Не могу отменить оффер #' + offer.offerid + ' Log: ' + err)
  587. declineProcceed = false;
  588. }
  589. });
  590. }
  591.  
  592.  
  593. var queueProceed = function(){
  594. redisClient.llen(redisChannels.checkList, function(err, length) {
  595. if (length > 0 && !checkProcceed && WebSession) {
  596. console.tag('SteamBot','Queues', logTime()).info('Проверяем Офферы:' + length);
  597. checkProcceed = true;
  598. checkOfferPrice();
  599. }
  600. });
  601. redisClient.llen(redisChannels.declineList, function(err, length) {
  602. if(length > 0 && !declineProcceed && WebSession) {
  603. console.tag('SteamBot','Queues', logTime()).info('Отклоняем Офферы:' + length);
  604. declineProcceed = true;
  605. redisClient.lindex(redisChannels.declineList, 0,function (err, offer) {
  606. declineOffersProcceed(offer);
  607. });
  608. }
  609. });
  610. redisClient.llen(redisChannels.checkedList, function(err, length) {
  611. if(length > 0 && !checkedProcceed && WebSession) {
  612. console.tag('SteamBot','Queues', logTime()).info('Проверенные Офферы:' + length);
  613. checkedProcceed = true;
  614. redisClient.lindex(redisChannels.checkedList, 0,function (err, offer) {
  615. checkedOffersProcceed(offer);
  616. });
  617. }
  618. });
  619. redisClient.llen(redisChannels.betsList, function(err, length) {
  620. if (length > 0 && !betsProcceed && !delayForNewGame) {
  621. console.tag('SteamBot','Queues', logTime()).info('Ставки:' + length);
  622. betsProcceed = true;
  623. checkNewBet();
  624. }
  625. });
  626. redisClient.llen(redisChannels.sendOffersList, function(err, length) {
  627. if (length > 0 && !sendProcceed && WebSession) {
  628. console.tag('SteamBot','Queues', logTime()).info('Отправляем победителю оффер:' + length);
  629. sendProcceed = true;
  630. redisClient.lindex(redisChannels.sendOffersList, 0,function (err, offerJson) {
  631. offer = JSON.parse(offerJson);
  632. sendTradeOffer(offer.appId, offer.steamid, offer.accessToken, offer.items, '', offer.game, offerJson);
  633. });
  634. }
  635. });
  636. redisClient.llen(redisChannels.checkItemsList, function(err, length) {
  637. if (length > 0 && !parseItemsProcceed && WebSession) {
  638. console.tag('SteamBot','Queues', logTime()).info('Парсим предметы:' + length);
  639. parseItemsProcceed = true;
  640. redisClient.lindex(redisChannels.checkItemsList, 0, function (err, offerJson) {
  641. offer = JSON.parse(offerJson);
  642. parseOffer(offer, offerJson);
  643. });
  644. }
  645. });
  646. }
  647. var parseItemsProcceed = false;
  648. var checkProcceed = false;
  649. var checkedProcceed = false;
  650. var declineProcceed = false;
  651. var betsProcceed = false;
  652. var sendProcceed = false;
  653. var delayForNewGame = false;
  654. setInterval(queueProceed, 1500);
  655.  
  656. module.exports.handleOffers = handleOffers;
  657. module.exports.delayForNewGame = function(value){
  658. delayForNewGame = value;
  659. };
  660.  
  661. function getErrorCode(err, callback){
  662. var errCode = 0;
  663. var match = err.match(/\(([^()]*)\)/);
  664. if(match != null && match.length == 2) errCode = match[1];
  665. callback(errCode);
  666. }
  667.  
  668. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement