· 8 years ago · Aug 01, 2017, 02:34 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, requestify;
13
14module.exports.init = function (redis, requestifyCore) {
15 redisClient = redis.createClient();
16 requestify = requestifyCore;
17}
18
19var details = {
20 account_name: config.shop.username,
21 password: config.shop.password,
22 two_factor_code: generatekey(config.shop.shared_secret)
23};
24
25var steamClient = new Steam.SteamClient();
26var steamUser = new Steam.SteamUser(steamClient);
27var steamFriends = new Steam.SteamFriends(steamClient);
28var steamWebLogOn = new SteamWebLogOn(steamClient, steamUser);
29var offers = new SteamTradeOffers();
30
31// Generation Device_ID
32var hash = require('crypto').createHash('sha1');
33hash.update(Math.random().toString());
34hash = hash.digest('hex');
35var device_id = 'android:' + hash;
36
37var checkingOffers = [],
38 WebCookies = [],
39 WebSession = false,
40 globalSession;
41
42const redisChannels = {
43 itemsToSale: 'items.to.sale',
44 itemsToCheck: 'items.to.check',
45 itemsToGive: 'items.to.give',
46 offersToCheck: 'offers.to.check'
47}
48
49function siteShopLogger(log) {
50 console.tag('Магазин').log(log);
51}
52
53function generatekey(secret) {
54 code = SteamTotp.generateAuthCode(secret);
55 siteShopLogger('Код Ðвторизации: ' + code);
56 return code;
57}
58
59steamClient.connect();
60steamClient.on('debug', siteShopLogger);
61steamClient.on('connected', function () {
62 steamUser.logOn(details);
63});
64
65steamFriends.on('friendMsg', function(steamID, message, type) {
66 if (WebSession){
67 if (config.admins.indexOf(steamID) == -1) return;
68 if (message.indexOf("/update") == 0) {
69 steamFriends.sendMessage(steamID, "Магазин обновлÑетÑÑ");
70 MyInvToSite();
71 }
72 }
73});
74
75steamClient.on('logOnResponse', function (logonResp) {
76 if (logonResp.eresult === Steam.EResult.OK) {
77 siteShopLogger('Вход выполнен!');
78 steamFriends.setPersonaState(Steam.EPersonaState.Online);
79
80 steamWebLogOn.webLogOn(function (sessionID, newCookie) {
81 getSteamAPIKey({
82 sessionID: sessionID,
83 webCookie: newCookie
84 }, function (err, APIKey) {
85 offers.setup({
86 sessionID: sessionID,
87 webCookie: newCookie,
88 APIKey: APIKey
89 }, function (err) {
90 WebSession = true;
91 globalSession = sessionID;
92 WebCookies = newCookie;
93 redisClient.lrange(redisChannels.tradeoffersList, 0, -1, function (err, offers) {
94 offers.forEach(function (offer) {
95 checkingOffers.push(offer);
96 });
97 handleOffers();
98 AcceptMobileOffer();
99 });
100 siteShopLogger('Обмены доÑтупны!');
101 //reWebLogonShop();
102 });
103
104 });
105 });
106 }
107});
108
109function reWebLogonShop() {
110 steamWebLogOn.webLogOn(function (sessionID, newCookie) {
111 getSteamAPIKey({
112 sessionID: sessionID,
113 webCookie: newCookie
114 }, function (err, APIKey) {
115 offers.setup({
116 sessionID: sessionID,
117 webCookie: newCookie,
118 APIKey: APIKey
119 }, function (err) {
120 WebSession = true;
121 globalSession = sessionID;
122 WebCookies = newCookie;
123 siteShopLogger('СеÑÑÐ¸Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð³Ñ€ÑƒÐ¶ÐµÐ½Ð° !');
124 AcceptMobileOffer()
125 });
126 });
127 });
128}
129
130steamClient.on('servers', function (servers) {
131 fs.writeFile('servers', JSON.stringify(servers));
132});
133
134function handleOffers() {
135 offers.getOffers({
136 get_received_offers: 1,
137 active_only: 1
138 }, function (error, body) {
139 if (body && body.response && body.response.trade_offers_received) {
140 body.response.trade_offers_received.forEach(function (offer) {
141 if (offer.trade_offer_state == 2) {
142 if(offer.items_to_receive != null && offer.items_to_give == null || config.admins.indexOf(offer.steamid_other) != -1){
143 siteShopLogger('Принимаем обмен #' + offer.tradeofferid + ' от: ' + offer.steamid_other );
144 offers.acceptOffer({
145 tradeOfferId: offer.tradeofferid
146 }, function (error, traderesponse) {
147 setTimeout(function () { AcceptMobileOffer() }, 5000);
148 if (!error) {
149 if ('undefined' != typeof traderesponse.tradeid) {
150 offers.getItems({
151 tradeId: traderesponse.tradeid
152 }, function (error, recieved_items) {
153 if (!error) {
154 var itemsForParse = [], itemsForSale = [], i = 0;
155 recieved_items.forEach(function (item) {
156 itemsForParse[i++] = item.id;
157 })
158 offers.loadMyInventory({
159 appId: 730,
160 contextId: 2,
161 language: 'russian'
162 }, function (error, botItems) {
163 if (!error) {
164 i = 0;
165 botItems.forEach(function (item) {
166 if (itemsForParse.indexOf(item.id) != -1) {
167 var rarity = '', type = '';
168 var arr = item.type.split(',');
169 if (arr.length == 2) rarity = arr[1].trim();
170 if (arr.length == 3) rarity = arr[2].trim();
171 if (arr.length && arr[0] == 'Ðож') rarity = 'Тайное';
172 if (arr.length) type = arr[0];
173 var quality = item.market_name.match(/\(([^()]*)\)/);
174 if (quality != null && quality.length == 2) quality = quality[1];
175 itemsForSale[i++] = {
176 userid: offer.steamid_other,
177 inventoryId: item.id,
178 classid: item.classid,
179 name: item.name,
180 market_hash_name: item.market_hash_name,
181 rarity: rarity,
182 quality: quality,
183 type: type
184 }
185 }
186 });
187 }
188 redisClient.rpush(redisChannels.itemsToSale, JSON.stringify(itemsForSale));
189 return;
190 });
191 }
192 return;
193 });
194 }
195 } else {
196 siteShopLogger('С Ошибкой: '+ error);
197 reWebLogonShop();
198 }
199 return;
200 });
201 } else {
202 offers.declineOffer({tradeOfferId: offer.tradeofferid});
203 }
204 return;
205 }
206 });
207 }
208 });
209}
210
211steamUser.on('tradeOffers', function (number) {
212 console.log('Предложений обмена: ' + number);
213 if (number > 0) {
214 handleOffers();
215 }
216});
217
218var sendTradeOffer = function(offerJson) {
219 var offer = JSON.parse(offerJson);
220 console.log('ОтправлÑем обмен: ' + offer.steamid);
221 try {
222 offers.loadMyInventory({
223 appId: 730,
224 contextId: 2
225 }, function(err, items) {
226 if (err) {
227 console.log(err);
228 sendProcceed = false;
229 return;
230 }
231 var itemsFromMe = [],
232 checkArr = [],
233 num = 0;
234 var i = 0;
235 console.tag('Магазин', 'Обмены').log('Предметы :' + JSON.stringify(offer.items));
236 for (var i = 0; i < offer.items.length; i++) {
237 for (var j = 0; j < items.length; j++) {
238 if (items[j].tradable && (items[j].id == offer.items[i])) {
239 if (checkArr.indexOf(items[j].id) == -1) {
240 checkArr[i] = items[j].id;
241 itemsFromMe[num] = {
242 appid: 730,
243 contextid: 2,
244 amount: items[j].amount,
245 assetid: items[j].id
246 };
247 num++;
248 break;
249 }
250 }
251 }
252 }
253 if (num > 0) {
254 offers.makeOffer({
255 partnerSteamId: offer.steamid,
256 accessToken: offer.accessToken,
257 itemsFromMe: itemsFromMe,
258 itemsFromThem: [],
259 message: 'Получите, раÑпишитеÑÑŒ :) ' + config.nameSite
260 }, function(err, response) {
261 if (err) {
262 console.log('Err: ' + err);
263 getErrorCode(err.message, function (errCode) {
264 if (errCode == 15 || errCode == 25 || err.message.indexOf('an error sending your trade offer. Please try again later.')) {
265 redisClient.lrem(redisChannels.itemsToGive, 0, offerJson, function (err, data) {
266 sendProcceed = false;
267 });
268 sendProcceed = false;
269 }
270 for (var i = 0; i < offer.items.length; i++) {
271 setItemStatus(offer.items[i], 4);
272 }
273 sendProcceed = false;
274 });
275 sendProcceed = false;
276 reWebLogonShop();
277 } else if (response) {
278 redisClient.lrem(redisChannels.itemsToGive, 0, offerJson, function (err, data) {
279 sendProcceed = false;
280 AcceptMobileOffer();
281 for (var i = 0; i < offer.items.length; i++) {
282 setItemStatus(offer.items[i], 3);
283 }
284 console.tag('Магазин', 'Обмены').log('Трейд #' + response.tradeofferid + ' Отправлен!');
285 redisClient.rpush(redisChannels.offersToCheck, response.tradeofferid);
286 });
287 }
288 });
289 } else {
290 console.tag('Магазин', 'Обмены').log('Предметы не найдены!');
291 for (var i = 0; i < offer.items.length; i++) {
292 setItemStatus(offer.items[i], 2);
293 }
294 redisClient.lrem(redisChannels.itemsToGive, 0, offerJson, function (err, data) {
295 sendProcceed = false;
296 });
297 }
298 });
299
300 } catch (ex) {
301 console.tag('Магазин').error('Ошибка отправки предмета');
302 sendProcceed = false;
303 reWebLogonShop();
304 }
305};
306
307var MyInvToSite = function() {
308 console.tag('Магазин').log('ОбновлÑем инвентарь и ÑпиÑок предметов на Ñайте');
309 try {
310 var itemsForCheck = [], i = 0;
311 offers.loadMyInventory({
312 appId: 730,
313 contextId: 2,
314 language: 'russian'
315 }, function (error, botItems) {
316 if (!error) {
317 i = 0;
318 botItems.forEach(function (item) {
319 if (item.tradable) {
320 var rarity = '', type = '';
321 var arr = item.type.split(',');
322 if (arr.length == 2) rarity = arr[1].trim();
323 if (arr.length == 3) rarity = arr[2].trim();
324 if (arr.length && arr[0] == 'Ðож') rarity = 'Тайное';
325 if (arr.length) type = arr[0];
326 var quality = item.market_name.match(/\(([^()]*)\)/);
327 if (quality != null && quality.length == 2) quality = quality[1];
328 itemsForCheck[i++] = {
329 inventoryId: item.id,
330 classid: item.classid,
331 name: item.name,
332 market_hash_name: item.market_hash_name,
333 rarity: rarity,
334 quality: quality,
335 type: type
336 }
337 }
338 });
339 }
340 redisClient.rpush(redisChannels.itemsToCheck, JSON.stringify(itemsForCheck));
341 return;
342 });
343 } catch (ex) {
344 console.tag('Магазин').error('Ошибка обновлениÑ');
345 sendProcceed = false;
346 }
347};
348
349function AcceptMobileOffer() {
350 // Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð´Ð»Ñ Ð¼Ð¾Ð±Ð¸Ð»ÑŒÐ½Ñ‹Ñ… подтверждений
351 var steamcommunityMobileConfirmations = new SteamcommunityMobileConfirmations({
352 steamid: config.shop.steamid,
353 identity_secret: config.shop.identity_secret,
354 device_id: device_id,
355 webCookie: WebCookies,
356 });
357
358 steamcommunityMobileConfirmations.FetchConfirmations((function (err, confirmations) {
359 if (err) {
360 console.log(err);
361 reWebLogonShop();
362 return;
363 }
364 console.tag('SiteShop', 'MobileTrades').log('Wait Offers: ' + confirmations.length);
365 if (!confirmations.length) {
366 return;
367 }
368 steamcommunityMobileConfirmations.AcceptConfirmation(confirmations[0], (function (err, result) {
369 if (err) {
370 console.log(err);
371 reWebLogonShop();
372 return;
373 }
374 clearInterval(acceptInterval);
375 console.tag('SiteShop', 'MobileTrades').log('Accept result: ' + result);
376 }).bind(this));
377 }).bind(this));
378}
379
380var setItemStatus = function (item, status) {
381 requestify.post('http://' + config.domain + '/api/shop/setItemStatus', {
382 secretKey: config.secretKey,
383 id: item,
384 status: status
385 })
386 .then(function (response) {
387 }, function (response) {
388 console.tag('Магазин').error('Something wrong with setItemStatus. Retry...');
389 setTimeout(function () {
390 setItemStatus()
391 }, 1000);
392 });
393}
394
395var addNewItems = function () {
396 requestify.post('http://' + config.domain + '/api/shop/newItems', {
397 secretKey: config.secretKey
398 })
399 .then(function (response) {
400 var answer = JSON.parse(response.body);
401 if (answer.success) {
402 console.tag('Магазин').error('Предметы добавлены на Ñайт !');
403 itemsToSaleProcced = false;
404 }
405 else {
406 console.tag('Магазин').error(answer);
407 }
408 }, function (response) {
409 console.tag('Магазин').error('Something wrong with newItems. Retry...');
410 setTimeout(function () {
411 addNewItems()
412 }, 1000);
413 });
414}
415
416var addCheckItems = function () {
417 requestify.post('http://' + config.domain + '/api/shop/checkShop', {
418 secretKey: config.secretKey
419 })
420 .then(function (response) {
421 var answer = JSON.parse(response.body);
422 if (answer.success) {
423 console.tag('Магазин').error('Проверка завершена уÑпешно!');
424 itemsToCheckProcced = false;
425 }
426 else {
427 console.tag('Магазин').error(answer);
428 }
429 }, function (response) {
430 console.tag('Магазин').error('Something wrong with check. Retry...');
431 setTimeout(function () {
432 addCheckItems()
433 }, 1000);
434 });
435}
436
437var checkOfferForExpired = function (offer) {
438 offers.getOffer({tradeOfferId: offer}, function (err, body) {
439 if ('undefined' != typeof body.response.offer ) {
440 var offerCheck = body.response.offer;
441 if (offerCheck.trade_offer_state == 2) {
442 var timeCheck = Math.floor(Date.now() / 1000) - offerCheck.time_created;
443 console.log(timeCheck);
444 if (timeCheck >= config.shop.timeForCancelOffer) {
445 if ('undefined' != typeof offer.items ) {
446 for (var i = 0; i < offer.items.length; i++) {
447 setItemStatus(offer.items[i], 4);
448 }
449 }
450 offers.cancelOffer({tradeOfferId: offer}, function (err, response) {
451 if (!err) {
452 redisClient.lrem(redisChannels.offersToCheck, 0, offer, function (err, data) {
453 siteShopLogger('Offer #' + offer + ' was expired!');
454 checkProcceed = false;
455
456 });
457 } else {
458 checkProcceed = false;
459 }
460 });
461 for (var i = 0; i < offerCheck.items_to_give.length; i++) {
462 setItemStatus(offerCheck.items_to_give[i].assetid, 0);
463 }
464 } else {
465 checkProcceed = false;
466 }
467 return;
468 } else if (offerCheck.trade_offer_state == 3 || offerCheck.trade_offer_state == 7) {
469 redisClient.lrem(redisChannels.offersToCheck, 0, offer, function (err, data) {
470 checkProcceed = false;
471 });
472 } else {
473 redisClient.lrem(redisChannels.offersToCheck, 0, offer, function (err, data) {
474 checkProcceed = false;
475 });
476 checkProcceed = false;
477 }
478 } else {
479 checkProcceed = false;
480 }
481 })
482}
483
484var queueProceed = function () {
485 redisClient.llen(redisChannels.itemsToSale, function (err, length) {
486 if (length > 0 && !itemsToSaleProcced) {
487 console.tag('Магазин', 'Ожидание').info('New items to sale:' + length);
488 itemsToSaleProcced = true;
489 addNewItems();
490 }
491 });
492 redisClient.llen(redisChannels.itemsToCheck, function (err, length) {
493 if (length > 0 && !itemsToCheckProcced) {
494 console.tag('Магазин', 'Ожидание').info('New items to check:' + length);
495 itemsToCheckProcced = true;
496 addCheckItems();
497 }
498 });
499 redisClient.llen(redisChannels.itemsToGive, function (err, length) {
500 if (length > 0 && !sendProcceed && WebSession) {
501 console.tag('Магазин', 'Ожидание').info('Send items:' + length);
502 sendProcceed = true;
503 redisClient.lindex(redisChannels.itemsToGive, 0, function (err, offerJson) {
504 sendTradeOffer(offerJson);
505 });
506 }
507 });
508 redisClient.llen(redisChannels.offersToCheck, function (err, length) {
509 if (length > 0 && !checkProcceed && WebSession) {
510 console.tag('Магазин', 'Ожидание').info('Проверка предложений:' + length);
511 checkProcceed = true;
512 redisClient.lindex(redisChannels.offersToCheck, 0, function (err, offer) {
513 checkOfferForExpired(offer);
514 });
515 }
516 });
517}
518
519var itemsToSaleProcced = false;
520var itemsToCheckProcced = false;
521var sendProcceed = false;
522var checkProcceed = false;
523setInterval(queueProceed, 30000);
524//setInterval(MyInvToSite, config.shop.timeForCancelOffer * 500);
525function getErrorCode(err, callback) {
526 var errCode = 0;
527 var match = err.match(/\(([^()]*)\)/);
528 if (match != null && match.length == 2) errCode = match[1];
529 callback(errCode);
530}