· 9 years ago · Feb 12, 2017, 07:38 PM
1var fs = require('fs');
2var crypto = require('crypto');
3var console = process.console;
4var config = require('./config.js');
5var Steam = require('steam');
6var SteamWebLogOn = require('steam-weblogon');
7var getSteamAPIKey = require('steam-web-api-key');
8var SteamTradeOffers = require('steam-tradeoffers');
9var SteamCommunity = require('steamcommunity');
10var SteamcommunityMobileConfirmations = require('steamcommunity-mobile-confirmations');
11var SteamTotp = require('steam-totp');
12var redisClient, io, requestify;
13
14module.exports.init = function (redis, ioSocket, requestifyCore) {
15 io = ioSocket;
16 redisClient = redis.createClient();
17 requestify = requestifyCore;
18}
19
20var details = {
21 account_name: config.bot.username,
22 password: config.bot.password,
23 two_factor_code: generatekey(config.bot.secret)
24};
25
26var steamClient = new Steam.SteamClient();
27var steamUser = new Steam.SteamUser(steamClient);
28var steamFriends = new Steam.SteamFriends(steamClient);
29var steamWebLogOn = new SteamWebLogOn(steamClient, steamUser);
30var offers = new SteamTradeOffers();
31
32// Generation Device_ID
33var hash = require('crypto').createHash('sha1');
34hash.update(Math.random().toString());
35hash = hash.digest('hex');
36var device_id = 'android:' + hash;
37
38var checkingOffers = [],
39 WebCookies = [],
40 WebSession = false,
41 globalSession;
42
43const redisChannels = {
44 checkItemsList: 'checkItems.list',
45 checkList: 'check.list',
46 checkedList: 'checked.list',
47 betsList: 'bets.list',
48 sendOffersList: 'send.offers.list',
49 tradeoffersList: 'tradeoffers.list',
50 declineList: 'decline.list',
51 usersQueue: 'usersQueue.list'
52}
53
54function steamBotLogger(log) {
55 console.tag('SteamBot').log(log);
56}
57
58function EscrowLogger(log) {
59 console.tag('EscrowBot').info(log);
60}
61
62function generatekey(secret) {
63 code = SteamTotp.generateAuthCode(secret);
64 EscrowLogger('Generated Code : ' + code);
65 return code;
66}
67
68steamClient.connect();
69steamClient.on('debug', steamBotLogger);
70steamClient.on('connected', function () {
71 steamUser.logOn(details);
72});
73
74steamClient.on('logOnResponse', function (logonResp) {
75 if (logonResp.eresult === Steam.EResult.OK) {
76 steamBotLogger('Logged in!');
77 steamFriends.setPersonaState(Steam.EPersonaState.Online);
78
79 steamWebLogOn.webLogOn(function (sessionID, newCookie) {
80 getSteamAPIKey({
81 sessionID: sessionID,
82 webCookie: newCookie
83 }, function (err, APIKey) {
84 offers.setup({
85 sessionID: sessionID,
86 webCookie: newCookie,
87 APIKey: APIKey
88 }, function (err) {
89 WebSession = true;
90 globalSession = sessionID;
91 WebCookies = newCookie;
92 redisClient.lrange(redisChannels.tradeoffersList, 0, -1, function (err, offers) {
93 offers.forEach(function (offer) {
94 checkingOffers.push(offer);
95 });
96 handleOffers();
97 AcceptMobileOffer();
98 });
99 steamBotLogger('Setup Offers!');
100 });
101
102 });
103 });
104 }
105});
106
107function reWebLogon() {
108 steamWebLogOn.webLogOn(function (sessionID, newCookie) {
109 getSteamAPIKey({
110 sessionID: sessionID,
111 webCookie: newCookie
112 }, function (err, APIKey) {
113 offers.setup({
114 sessionID: sessionID,
115 webCookie: newCookie,
116 APIKey: APIKey
117 }, function (err) {
118 WebSession = true;
119 globalSession = sessionID;
120 WebCookies = newCookie;
121 steamBotLogger('WebSession Reloaded !');
122 });
123 });
124 });
125}
126
127function handleOffers() {
128 offers.getOffers({
129 get_received_offers: 1,
130 active_only: 1
131 }, function (error, body) {
132 if (
133 body
134 && body.response
135 && body.response.trade_offers_received
136 ) {
137 body.response.trade_offers_received.forEach(function (offer) {
138 if (offer.trade_offer_state == 2) {
139 if (is_checkingOfferExists(offer.tradeofferid)) return;
140
141 if (offer.items_to_give != null && config.admins.indexOf(offer.steamid_other) != -1) {
142 console.tag('SteamBot', 'TradeOffer').log('TRADE OFFER #' + offer.tradeofferid + ' FROM: Admin ' + offer.steamid_other);
143 offers.acceptOffer({tradeOfferId: offer.tradeofferid});
144 return;
145 }
146 if (offer.items_to_give != null) {
147 offers.declineOffer({tradeOfferId: offer.tradeofferid});
148 return;
149 }
150 offers.getTradeHoldDuration({tradeOfferId: offer.tradeofferid}, function (err, hold) {
151 if (err) {
152 reWebLogon();
153 steamBotLogger('Check hold error: ' + err);
154 setTimeout(function () {
155 offers.getTradeHoldDuration({tradeOfferId: offer.tradeofferid}, function (err, hold) {
156 if (err) {
157 offers.declineOffer({tradeOfferId: offer.tradeofferid});
158 reWebLogon();
159 steamBotLogger('Check hold error: ' + err);
160 } else {
161 if (hold.their != 0) {
162 offers.declineOffer({tradeOfferId: offer.tradeofferid});
163 steamBotLogger('Escrow offer declined: ' + offer.tradeofferid);
164 }
165 }
166 });
167 }, 5000);
168 } else {
169 if (hold.their != 0) {
170 offers.declineOffer({tradeOfferId: offer.tradeofferid});
171 steamBotLogger('Escrow offer declined: ' + offer.tradeofferid);
172 }
173 }
174 });
175 if (offer.items_to_receive != null && offer.items_to_give == null) {
176 checkingOffers.push(offer.tradeofferid);
177 console.tag('SteamBot', 'TradeOffer').log('TRADE OFFER #' + offer.tradeofferid + ' FROM: ' + offer.steamid_other);
178 redisClient.multi([
179 ['rpush', redisChannels.tradeoffersList, offer.tradeofferid],
180 ['rpush', redisChannels.checkItemsList, JSON.stringify(offer)],
181 ['rpush', redisChannels.usersQueue, offer.steamid_other]
182 ]).exec(function () {
183 redisClient.lrange(redisChannels.usersQueue, 0, -1, function (err, queues) {
184 io.sockets.emit('queue', queues);
185 });
186 });
187 return;
188 }
189 }
190 });
191 }
192 });
193}
194
195steamUser.on('tradeOffers', function (number) {
196 if (number > 0) {
197 handleOffers();
198 }
199});
200
201
202var parseOffer = function (offer, offerJson) {
203 offers.loadPartnerInventory({
204 partnerSteamId: offer.steamid_other,
205 appId: 730,
206 contextId: 2,
207 tradeOfferId: offer.tradeofferid,
208 language: "russian"
209 }, function (err, hitems) {
210 if (err) {
211 redisClient.multi([
212 ['rpush', redisChannels.declineList, offer.tradeofferid],
213 ['lrem', redisChannels.checkItemsList, 0, offerJson],
214 ['lrem', redisChannels.usersQueue, 1, offer.steamid_other]
215 ])
216 .exec(function (err, replies) {
217 parseItemsProcceed = false;
218 return;
219 });
220 return;
221 }
222 var items = offer.items_to_receive;
223 var items_to_check = [], num = 0;
224 for (var i = 0; i < items.length; i++) {
225 for (var j = 0; j < hitems.length; j++) {
226 if (items[i].assetid == hitems[j].id) {
227 items_to_check[num] = {
228 appid: hitems[j].appid,
229 name: hitems[j].market_name,
230 market_hash_name: hitems[j].market_hash_name,
231 classid: hitems[j].classid
232 };
233 var type = hitems[j].type;
234 var rarity = '';
235 var arr = type.split(',');
236 if (arr.length == 2) type = arr[1].trim();
237 if (arr.length == 3) type = arr[2].trim();
238 if (arr.length && arr[0] == 'Ðож') type = '★';
239 switch (type) {
240 case 'ÐрмейÑкое качеÑтво':
241 rarity = 'milspec';
242 break;
243 case 'Запрещенное':
244 rarity = 'restricted';
245 break;
246 case 'ЗаÑекреченное':
247 rarity = 'classified';
248 break;
249 case 'Тайное':
250 rarity = 'covert';
251 break;
252 case 'Ширпотреб':
253 rarity = 'common';
254 break;
255 case 'Промышленное качеÑтво':
256 rarity = 'common';
257 break;
258 case '★':
259 rarity = 'rare';
260 break;
261 }
262 items_to_check[num].rarity = rarity;
263 num++;
264 break;
265 }
266 }
267 }
268 var value = {
269 offerid: offer.tradeofferid,
270 accountid: offer.steamid_other,
271 items: JSON.stringify(items_to_check)
272 };
273
274 console.tag('SteamBot', 'Offer #' + value.offerid).log(value);
275
276 redisClient.multi([
277 ['rpush', redisChannels.checkList, JSON.stringify(value)],
278 ['lrem', redisChannels.checkItemsList, 0, offerJson]
279 ])
280 .exec(function (err, replies) {
281 parseItemsProcceed = false;
282 });
283
284 });
285}
286
287var checkOfferPrice = function () {
288 requestify.post('http://' + config.domain + '/api/checkOffer', {
289 secretKey: config.secretKey
290 })
291 .then(function (response) {
292 var answer = JSON.parse(response.body);
293
294 if (answer.success) {
295 checkProcceed = false;
296 }
297 }, function (response) {
298 console.tag('SteamBot').error('Something wrong with check offers. Retry...');
299 setTimeout(function () {
300 checkOfferPrice()
301 }, 1000);
302 });
303
304}
305
306var checkNewBet = function () {
307 requestify.post('http://' + config.domain + '/api/newBet', {
308 secretKey: config.secretKey
309 })
310 .then(function (response) {
311 var answer = JSON.parse(response.body);
312 if (answer.success) {
313 betsProcceed = false;
314 }
315 }, function (response) {
316 console.tag('SteamBot').error('Something wrong with send a new bet. Retry...');
317 setTimeout(function () {
318 checkNewBet()
319 }, 1000);
320 });
321}
322
323var checkArrGlobal = [];
324
325var sendTradeOffer = function (appId, partnerSteamId, accessToken, sendItems, message, game, offerJson) {
326 try {
327 offers.loadMyInventory({
328 appId: appId,
329 contextId: 2
330 }, function (err, items) {
331 if (err) {
332 console.log(err);
333 sendProcceed = false;
334 return;
335 }
336 var itemsFromMe = [],
337 checkArr = [],
338 num = 0;
339 var i = 0;
340 for (var i = 0; i < sendItems.length; i++) {
341 for (var j = 0; j < items.length; j++) {
342 if (items[j].tradable && (items[j].classid == sendItems[i])) {
343 if ((checkArr.indexOf(items[j].id) == -1) && (checkArrGlobal.indexOf(items[j].id) == -1)) {
344 checkArr[i] = items[j].id;
345 itemsFromMe[num] = {
346 appid: 730,
347 contextid: 2,
348 amount: items[j].amount,
349 assetid: items[j].id
350 };
351 num++;
352 break;
353 }
354 }
355 }
356 }
357 if (num > 0) {
358 offers.makeOffer({
359 partnerSteamId: partnerSteamId,
360 accessToken: accessToken,
361 itemsFromMe: itemsFromMe,
362 itemsFromThem: [],
363 message: 'ПоздравлÑем Ñ Ð¿Ð¾Ð±ÐµÐ´Ð¾Ð¹ на Ñайте ' + config.nameSite + ' | Ð’ игре #' + game
364 }, function (err, response) {
365 if (err) {
366 console.tag('SteamBot', 'SendPrize').error('Error to send offer:' + err.message);
367 getErrorCode(err.message, function (errCode) {
368 if (errCode == 15 || errCode == 25 || err.message.indexOf('an error sending your trade offer. Please try again later.')) {
369 redisClient.lrem(redisChannels.sendOffersList, 0, offerJson, function (err, data) {
370 setPrizeStatus(game, 2);
371 sendProcceed = false;
372 });
373 sendProcceed = false;
374 }
375 sendProcceed = false;
376 });
377 return;
378 }
379 checkArrGlobal = checkArrGlobal.concat(checkArr);
380 redisClient.lrem(redisChannels.sendOffersList, 0, offerJson, function (err, data) {
381 setPrizeStatus(game, 1);
382 sendProcceed = false;
383 });
384 console.tag('SteamBot', 'SendPrize').log('TradeOffer #' + response.tradeofferid + ' send!');
385 AcceptMobileOffer();
386 });
387 } else {
388 console.tag('SteamBot', 'SendPrize').log('Items not found!');
389 redisClient.lrem(redisChannels.sendOffersList, 0, offerJson, function (err, data) {
390 setPrizeStatus(game, 1);
391 sendProcceed = false;
392 });
393 }
394 });
395
396 } catch (ex) {
397 console.tag('SteamBot').error('Error to send the bet');
398 setPrizeStatus(game, 2);
399 sendProcceed = false;
400 }
401};
402
403var setPrizeStatus = function (game, status) {
404 requestify.post('http://' + config.domain + '/api/setPrizeStatus', {
405 secretKey: config.secretKey,
406 game: game,
407 status: status
408 })
409 .then(function (response) {
410
411 }, function (response) {
412 console.tag('SteamBot').log('Something wrong with set prize status. Retry...');
413 setTimeout(function () {
414 setPrizeStatus()
415 }, 1000);
416 });
417}
418
419var is_checkingOfferExists = function (tradeofferid) {
420 for (var i = 0, len = checkingOffers.length; i < len; ++i) {
421 var offer = checkingOffers[i];
422 if (offer == tradeofferid) {
423 return true;
424 break;
425 }
426 }
427 return false;
428}
429
430var checkedOffersProcceed = function (offerJson) {
431 var offer = JSON.parse(offerJson);
432 if (offer.success) {
433 console.tag('SteamBot').log('Procceding accept: #' + offer.offerid);
434 offers.acceptOffer({tradeOfferId: offer.offerid}, function (err, body) {
435 if (!err) {
436 redisClient.multi([
437 ["lrem", redisChannels.tradeoffersList, 0, offer.offerid],
438 ["lrem", redisChannels.usersQueue, 1, offer.steamid64],
439 ["rpush", redisChannels.betsList, offerJson],
440 ["lrem", redisChannels.checkedList, 0, offerJson]
441 ])
442 .exec(function (err, replies) {
443 redisClient.lrange(redisChannels.usersQueue, 0, -1, function (err, queues) {
444 io.sockets.emit('queue', queues);
445 console.tag('SteamBot').log("New bet Accepted!");
446 checkedProcceed = false;
447 });
448 });
449 AcceptMobileOffer();
450 } else {
451 console.tag('SteamBot').error('Error. With accept tradeoffer #' + offer.offerid);
452 offers.declineOffer({tradeOfferId: offer.offerid});
453
454 offers.getOffer({tradeOfferId: offer.offerid}, function (err, body) {
455 if (body.response.offer) {
456 var offerCheck = body.response.offer;
457 if (offerCheck.trade_offer_state == 2) {
458 checkedProcceed = false;
459 return;
460 }
461 if (offerCheck.trade_offer_state == 3) {
462 redisClient.multi([
463 ["lrem", redisChannels.tradeoffersList, 0, offer.offerid],
464 ["lrem", redisChannels.usersQueue, 1, offer.steamid64],
465 ["rpush", redisChannels.betsList, offerJson],
466 ["lrem", redisChannels.checkedList, 0, offerJson]
467 ])
468 .exec(function (err, replies) {
469 redisClient.lrange(redisChannels.usersQueue, 0, -1, function (err, queues) {
470 io.sockets.emit('queue', queues);
471 checkedProcceed = false;
472 });
473 });
474 } else {
475 redisClient.multi([
476 ["lrem", redisChannels.tradeoffersList, 0, offer.offerid],
477 ["lrem", redisChannels.usersQueue, 1, offer.steamid64],
478 ["lrem", redisChannels.checkedList, 0, offerJson]
479 ])
480 .exec(function (err, replies) {
481 redisClient.lrange(redisChannels.usersQueue, 0, -1, function (err, queues) {
482 io.sockets.emit('queue', queues);
483 checkedProcceed = false;
484 });
485 });
486 }
487 }
488 })
489 }
490 });
491 }
492}
493
494function AcceptMobileOffer() {
495 // Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð´Ð»Ñ Ð¼Ð¾Ð±Ð¸Ð»ÑŒÐ½Ñ‹Ñ… подтверждений
496 var steamcommunityMobileConfirmations = new SteamcommunityMobileConfirmations(
497 {
498 steamid: config.bot.steamid,
499 identity_secret: config.bot.identity_secret,
500 device_id: device_id,
501 webCookie: WebCookies,
502 });
503
504 steamcommunityMobileConfirmations.FetchConfirmations((function (err, confirmations) {
505 if (err) {
506 console.log(err);
507 return;
508 }
509 console.tag('SteamBot', 'MobileTrades').log('Wait Offers: ' + confirmations.length);
510 if (!confirmations.length) {
511 return;
512 }
513 steamcommunityMobileConfirmations.AcceptConfirmation(confirmations[0], (function (err, result) {
514 if (err) {
515 console.log(err);
516 return;
517 }
518 console.tag('SteamBot', 'MobileTrades').log('Accept result: ' + result);
519 }).bind(this));
520 }).bind(this));
521}
522
523var declineOffersProcceed = function (offerid) {
524 console.tag('SteamBot').log('Procceding decline: #' + offerid);
525 offers.declineOffer({tradeOfferId: offerid}, function (err, body) {
526 if (!err) {
527 console.tag('SteamBot').log('Offer #' + offerid + ' Decline!');
528 redisClient.lrem(redisChannels.declineList, 0, offerid);
529 declineProcceed = false;
530 } else {
531 console.tag('SteamBot').error('Error. With decline tradeoffer #' + offer.offerid)
532 .tag('SteamBot').log(err);
533 declineProcceed = false;
534 }
535 });
536}
537
538var queueProceed = function () {
539 redisClient.llen(redisChannels.checkList, function (err, length) {
540 if (length > 0 && !checkProcceed) {
541 console.tag('SteamBot', 'Queues').info('CheckOffers:' + length);
542 checkProcceed = true;
543 checkOfferPrice();
544 }
545 });
546 redisClient.llen(redisChannels.checkedList, function (err, length) {
547 if (length > 0 && !checkedProcceed && WebSession) {
548 console.tag('SteamBot', 'Queues').info('CheckedOffers:' + length);
549 checkedProcceed = true;
550 redisClient.lindex(redisChannels.checkedList, 0, function (err, offer) {
551 checkedOffersProcceed(offer);
552 });
553 }
554 });
555 redisClient.llen(redisChannels.declineList, function (err, length) {
556 if (length > 0 && !declineProcceed && WebSession) {
557 console.tag('SteamBot', 'Queues').info('DeclineOffers:' + length);
558 declineProcceed = true;
559 redisClient.lindex(redisChannels.declineList, 0, function (err, offer) {
560 declineOffersProcceed(offer);
561 });
562 }
563 });
564 redisClient.llen(redisChannels.betsList, function (err, length) {
565 if (length > 0 && !betsProcceed && !delayForNewGame) {
566 console.tag('SteamBot', 'Queues').info('Bets:' + length);
567 betsProcceed = true;
568 checkNewBet();
569 }
570 });
571 redisClient.llen(redisChannels.sendOffersList, function (err, length) {
572 if (length > 0 && !sendProcceed && WebSession) {
573 console.tag('SteamBot', 'Queues').info('Send winner offers:' + length);
574 sendProcceed = true;
575 redisClient.lindex(redisChannels.sendOffersList, 0, function (err, offerJson) {
576 offer = JSON.parse(offerJson);
577 sendTradeOffer(offer.appId, offer.steamid, offer.accessToken, offer.items, '', offer.game, offerJson);
578 });
579 }
580 });
581 redisClient.llen(redisChannels.checkItemsList, function (err, length) {
582 if (length > 0 && !parseItemsProcceed && WebSession) {
583 console.tag('SteamBot', 'Queues').info('Parse items:' + length);
584 parseItemsProcceed = true;
585 redisClient.lindex(redisChannels.checkItemsList, 0, function (err, offerJson) {
586 offer = JSON.parse(offerJson);
587 parseOffer(offer, offerJson);
588 });
589 }
590 });
591}
592var parseItemsProcceed = false;
593var checkProcceed = false;
594var checkedProcceed = false;
595var declineProcceed = false;
596var betsProcceed = false;
597var sendProcceed = false;
598var delayForNewGame = false;
599setInterval(queueProceed, 1500);
600
601module.exports.handleOffers = handleOffers;
602module.exports.delayForNewGame = function (value) {
603 delayForNewGame = value;
604};
605
606function getErrorCode(err, callback) {
607 var errCode = 0;
608 var match = err.match(/\(([^()]*)\)/);
609 if (match != null && match.length == 2) errCode = match[1];
610 callback(errCode);
611}