UNPKG

31.8 kBJavaScriptView Raw
1/*
2HOTELPARTNER
3Autor: HotelPartner WDS
4Author's note: “Clean code always looks like it was written by someone who cares.” ― Robert C. Martin,
5
6Date: 16.08.2017
7Version: 2.0
8Notes: Make sure that the hostname is set correct!
9===============================
10
11 */
12
13
14//HP LIBARIES
15const _HP = require("./classHotels.js");
16const _CONTROLLER = require("./bot_controller.js");
17
18const slave = _CONTROLLER.os.hostname().replace('slave','');
19const socket = _CONTROLLER.io.connect('http://192.168.254.89:88/', {
20 query:'slave='+slave,
21 reconnect: true
22});
23
24//Global Variables
25
26let currentip = '0.0.0.0';
27let errjobcounter = 0;
28let ipischanging = false;
29let client;
30let mobile;
31let api;
32let hostip;
33let hostport;
34
35
36// ##ENUMS
37const ADULT = {
38 ERROR: 0,
39 ONE : 1,
40 TWO : 2
41};
42const JOB = {
43 DONE:1,
44 CITY_ID_LEFT:0
45};
46
47// ## PROTOTYPING
48Number.prototype.isActive = function () {
49 return(this!=-1)
50};
51Object.prototype.prepareNextRequest = function (callback) {
52
53 const arg = this;
54
55 //check if job is valid
56 if(arg.cityid[0]=='' || !arg.cityid[0] || !arg.dates[0]){
57 hp_bot.job.getNewJob(true)
58 // _CONTROLLER.reboot();
59 return null;
60 }
61
62 //repace all spaces with +
63 let city = arg.name.split(' ').join('+');
64 let countcity = arg.cityid.length-1;
65 let countdate = arg.dates.length-1;
66
67
68 callback(arg.dates[countdate], city ,arg.cityid[countcity]);
69};
70
71
72const hp_bot = {
73 "constructor": function JOB(_api=-1,_mobile=-1,_client='WDS',_host='192.168.56.1',_port='88') {
74
75 //Set Variables
76 api = _api;
77 mobile = _mobile;
78 client = _client;
79 hostip = _host;
80 hostport = _port;
81
82
83 //START SERVER
84 _CONTROLLER.start();
85 //Tell Host we are online
86 socket.emit('slaves',slave)
87
88
89 setInterval(()=>{
90 socket.emit(slave,'says hello')
91 },1000)
92 },
93 "init": function init() {
94 setTimeout( async function start(){
95
96 //get job
97 let job = await hp_bot.job.searchJob()
98 currentip = await _CONTROLLER.showExternalIP(true);
99 // here we should do a foreach to get each cityid and each Date
100 async function startForeach()
101 {
102 job.prepareNextRequest(async function (startdate,cityname,cityid) {
103
104 var bot = -5;
105
106 if(mobile.isActive())
107 bot = await hp_bot.bot.mobile(job,startdate,cityname,cityid);
108
109 else if(api.isActive())
110 bot = await hp_bot.bot.api(job,startdate,cityname,cityid)
111
112 else
113 bot = await hp_bot.bot.standart(job,startdate,cityname,cityid);
114
115 //get next job
116
117 if( bot == JOB.DONE )
118 {
119
120 console.log("=> JOB done <=")
121 _HP.hotels.Booking = []
122
123 hp_bot.job.saveJob([])
124 hp_bot.job.nextJob(hp_bot.init)
125 }
126
127 else if( bot == JOB.CITY_ID_LEFT)
128 {
129 setTimeout(async function(){
130 console.log("== next => ")
131 job = await hp_bot.job.getOldJob();
132 console.log(job)
133 startForeach();
134 },1000)
135
136 }
137
138 })
139 }
140
141 startForeach();
142
143 },1800)
144 },
145 job: {
146 'getNewJob' : function getNewJob(rebootOnError=true) {
147 console.log("");
148 console.log("GETTING NEW JOB FROM DB... CLIENT:" +client );
149
150 let data = "";
151 let clientVersion = '2217'
152
153 return new Promise(resolve=>{
154
155 //check clientverison and callback to _clientVersionCallB
156 // with the correct clientVersion
157 function makeRequest(clientversion){
158 console.log('clientversion '+clientversion)
159
160 _CONTROLLER.http.get('http://www.yieldagent.ch/relay/cityov/requestJobV8.php?version='+clientVersion+'&client='+client, function(res) {
161 if (res.statusCode >= 200 && res.statusCode < 400)
162 {
163 res.on('data', function(data_) { data += data_.toString(); });
164 res.on('end', function() {
165 _CONTROLLER.parseXML(data, function (err, result) {
166
167 try
168 {
169 if (result)
170 {
171 //set newclient version
172 clientVersion = result.Request.$.clientVersion;
173
174 //save client version
175 _CONTROLLER.fs.writeFile('./clientVersion.txt', clientVersion, 'utf8', function readFileCallback(err, data) {
176 if (err)
177 console.log(err);
178
179 else
180 console.log("new Client Version "+clientVersion)
181
182 });
183
184 //Parse XML
185 let ids = [];
186 let city = result.Request.CityConfig[0].URLArguments[0];
187 city = city.split(';city=');
188 //remove unnecessary elements
189 city.shift();
190 city.forEach(t => {
191 t = t.substr(0, t.indexOf(';'))
192 ids.push(t)
193 });
194
195 //build json
196 let job = {
197 "id": result.Request.CityConfig[0].CityID[0],
198 "cityid": ids,
199 "name": result.Request.CityConfig[0].CityName[0],
200 "dates": result.Request.Dates[0].Date,
201 "backup": result.Request.Dates[0].Date,
202 "job": result.Request.JobID[0],
203 "los": result.Request.$.los
204 };
205
206 //save job
207 hp_bot.job.saveJob(job);
208
209 //check for errors in JOB
210 if(!result.Request.Dates[0].Date){
211 hp_bot.general.errorHandler(job,1,_CONTROLLER.time().format('YYYY-MM-DD'),job.name,job.cityid,'{"error":"no date"}')
212 hp_bot.job.saveJob([]);
213 }
214 if(!result.Request.CityConfig[0].CityName[0]){
215 hp_bot.general.errorHandler(job,1,moment().format('YYYY-MM-DD'),'null',0,'{"error":"city"}')
216 hp_bot.job.saveJob([]);
217 }
218
219 resolve(job)
220 }
221 if(err)
222 {
223 console.log(err)
224 setTimeout(()=>{
225 _CONTROLLER.reboot();
226 },680000)
227 }
228 }
229 catch (TypeError)
230 {
231 //make request with the new clienversion
232 setTimeout( makeRequest(clientversion),8000);
233
234 //LOG
235 console.log("JOB parsing error")
236 console.log(result.Request)
237 console.log(" ")
238 console.log("getting new jeb with new clientVersion:" +clientVersion)
239
240
241 }
242
243 });
244 });
245 }
246 }).on('error',
247 async function(err) {
248
249 //Get current bot version
250 let version = await _CONTROLLER.version(false);
251
252 //Handle error
253 console.log("Error while fetching new Job");
254 console.log(err);
255
256 //request new ip
257 let hosturl = 'http://'+hostip+':'+hostport;
258 _CONTROLLER.fetch(hosturl+'/newip?gateway='+slave+'&client='+client+'&job=-1&version='+version);
259
260 // reboot if needed
261 setTimeout(()=>
262 {
263 if(errjobcounter >=3 && rebootOnError == true)
264 _CONTROLLER.fetch(hosturl+'/reboot?gateway='+slave+'&client='+client+'&job=error&version='+version);
265
266 else
267 _CONTROLLER.fetch(hosturl+'/newip?gateway='+slave+'&client='+client+'&version='+version).then(newIP=>{
268 hp_bot.job.getNewJob();
269 })
270
271 errjobcounter++;
272 },30000)
273 }
274 );
275 }
276
277 //Read clientsversion
278 _CONTROLLER.fs.readFile('./clientVersion.txt','utf8', function read(err, data)
279 {
280 if (err)
281 {
282 console.log('no file: clientVersion.txt')
283 makeRequest(clientVersion);
284 return;
285 }
286 else
287 {
288 clientVersion = data;
289 makeRequest(data)
290 }
291
292 });
293 })
294
295 },
296 'getOldJob' : function getOldJob() {
297 console.log("searching for old job");
298 try{
299 //Look for a backup file
300 const backup = JSON.parse(_CONTROLLER.fs.readFileSync('./backup.json', 'utf8'));
301
302 //Check if the file contains valid data
303 const checkforid = backup.cityid[0];
304 const checkfordate = backup.dates[0];
305 return backup;
306 }
307 catch (TypeError)
308 {
309 console.log("no Job found..")
310 return null
311 }
312 },
313 'searchJob' : async function () {
314 var job = hp_bot.job.getOldJob();
315
316 if(!job)
317 job = await hp_bot.job.getNewJob();
318 else
319 console.log("BACKUP JOB FOUND");
320
321 return job;
322 },
323 'updateOldJob' : function (date,cityid,resetdate=false) {
324 return new Promise(resolve=>{
325
326 try{
327
328 let backup = JSON.parse(_CONTROLLER.fs.readFileSync('./backup.json', 'utf8'));
329 const indx = backup.dates.indexOf(date);
330
331 //Update dates
332 if (indx > -1 && backup.dates.length>=1)
333 {
334 console.log("removing date")
335 backup.dates.splice(indx, 1);
336 console.log( " ")
337 console.log("****dates left: "+ backup.dates.length)
338 console.log( " ")
339 }
340
341 //Update cityid
342 if( backup.dates.length==0)
343 {
344 console.log("removing city id")
345 const indx2= backup.cityid.indexOf(cityid)
346 if (indx2 > -1){
347 backup.cityid.splice(indx2, 1);
348 backup.dates = backup.backup;
349 }
350
351 if(backup.cityid.length==0){
352 console.log("Clearing JOB")
353 hp_bot.job.saveJob([]).then(r=>{
354 resolve(JOB.DONE)
355 });
356
357 }
358
359
360 }
361
362 //Save the new Backup
363 hp_bot.job.saveJob(backup).then(r=>{
364 resolve(JOB.CITY_ID_LEFT)
365 });
366
367 }
368 catch (TypeError)
369 {
370 console.log('!!! JOB NOT SAVED !!!')
371 console.log(TypeError)
372 resolve(false)
373 }
374 })
375 },
376 'nextJob' : async function nextJob(callback) {
377 console.log("get new Job")
378 const newjob = await hp_bot.job.getNewJob();
379 callback(newjob);
380 },
381 'saveJob' : function (json) {
382 if(!json)return;
383 return new Promise(resolve=>{
384
385
386 console.log(" ")
387 console.log("Saving Job | backup.json");
388 _CONTROLLER.fs.writeFile('./backup.json', JSON.stringify(json), 'utf8', function readFileCallback(err, data) {
389 if (err)
390 console.log(err);
391 else
392 console.log("JOB is SAVED")
393
394 console.log(" ")
395 resolve(true)
396 })
397 });
398 },
399 'sendJobToHost': function (json_job,httpOrSocket='socket') {
400 return new Promise(resolve => {
401 if (httpOrSocket == 'socket') {
402 socket.emit('jobdone', JSON.stringify(json_job), function (response) {
403 console.log(response);
404 resolve(response)
405 });
406 }
407 })
408 },
409 },
410 bot: {
411 "mobile" : async function startMobileJob (arg,startdate,cityname,cityid,API=null) {
412
413 console.log("->Fetching Mobile Version");
414 const end = _CONTROLLER.time(startdate,'YYYY-MM-DD').add(1, 'day').format('YYYY-MM-DD');
415 let counterror = 0;
416 return new Promise(resolve=>{
417 let adult =1;
418
419 async function startFetch(adult) {
420 console.log(startdate,cityname,cityid,adult);
421
422 const xmlresult = await hp_bot.general.getXMLData(adult,startdate,end,cityname,cityid);
423
424 const isvalid = hp_bot.general.validateXmlResult(xmlresult,arg,adult,startdate,cityname,cityid);
425 if(!isvalid)return;
426
427 //Parse Data
428 const parsed = await _HP.methods.Booking.parsemobile(xmlresult.result,adult,startdate,end,cityname,cityid);
429
430 if(parsed==ADULT.ONE){
431 startFetch(2)
432 }
433 if(parsed==ADULT.TWO)
434 {
435 counterror = 0;
436 const json = await _HP.methods.general.generateJson(_HP.hotels.Booking, arg.job, arg.id, startdate, slave);
437
438 if(API)
439 {
440 API(json)
441 resolve(json)
442 }
443 else
444 {
445 const answer = await hp_bot.job.sendJobToHost(json, 'socket');
446 if (answer == 'OK')
447 {
448 console.log("SENDED: OK")
449 let _status = await hp_bot.job.updateOldJob(startdate, cityid);
450 resolve(_status)
451 }
452 else {
453 startFetch(adult);
454 }
455 }
456
457 }
458 if(parsed==ADULT.ERROR){
459
460 console.log("ip got blocked")
461
462 //check if there is a error with the url
463 if(4<counterror)
464 {
465 let message;
466 try{
467 message = allresult.errors[0].message;
468 hp_bot.general.errorHandler(arg,adult,startdate,cityname,cityid,message,'Mobile Parser',true)
469 }catch (TypeError)
470 {
471
472 if(JSON.stringify(allresult).indexOf('in_city_translation')>=0)
473 {
474 message = {'error':'in_city_translation no results found'}
475 hp_bot.general.errorHandler(arg,adult,startdate,cityname,cityid,message,'Mobile Parser',true)
476 }
477 else
478 {
479 message = {'info':'no results found'}
480 hp_bot.general.errorHandler(arg,adult,startdate,cityname,cityid,message,'Mobile Parser',true)
481 }
482 }
483
484 counterror = 0;
485 }
486
487 //standart IP: 192.168.56.1
488 setTimeout(()=>{
489 hp_bot.general.getNewIP(ipischanging).then(gotNewIP=>
490 {
491 counterror++
492 console.log('IP changes counter : '+counterror)
493 ipischanging=false;
494 startFetch(adult);
495
496 })
497 },1000)
498 ipischanging =true;
499
500 }
501
502 console.log("PARSED RESULT "+ parsed)
503
504
505 }
506 startFetch(adult);
507
508 })
509 },
510 "standart" : async function startSeleniumJOB(arg) {
511 let isfetching = true;
512 console.log("->Fetching Selenium Version");
513 console.log("Selenium is still in testing mode.. ");
514
515 //IF NO CITY ID go to NEXT JOB
516 if(arg.cityid[0]=='' || !arg.cityid[0])
517 {
518 hp_bot.job.nextJob(startSeleniumJOB);
519 return
520 }
521
522 console.log("JOB FOUND:");
523 console.log(arg);
524 console.log("=====================");
525 currentip = await _CONTROLLER.showExternalIP(true)
526
527 let city = arg.name.replace(" ", '+');
528 let icity = 0;
529 let countcity = arg.cityid.length;
530
531 let idate = 0;
532 let countdate = arg.dates.length;
533
534
535 nextCityId();
536 function nextCityId() {
537 //Check if we have made all CityID
538 if(icity==countcity && isfetching){
539 console.log("Job Done");
540 console.log(JSON.stringify(_HP.hotels.Booking));
541 hp_bot.job.nextJob(startSeleniumJOB);
542 }
543 //Start fetching next CityID
544 if(icity<countcity)
545 {
546 idate=0;
547 nextDate(arg.cityid[icity])
548 icity++;
549 }
550
551 }
552 function nextDate(cityid) {
553 //Clear after new date
554 _HP.hotels.Booking = [];
555 const start = arg.dates[idate];
556 fetchData(1,start,city,cityid);
557 idate++;
558 }
559 function fetchData(adult,start,city,cityid) {
560
561 const end = _CONTROLLER.time(start,'YYYY-MM-DD').add(1, 'day').format('YYYY-MM-DD');
562 let room = 1;
563 let child = 1;
564
565 _HP.methods.Booking.get(cityid,city,start,end,room,adult,child,false,arg.job,arg.id).then(result=>{
566
567 let isok = -1;
568
569 //go to next adult
570 if(isok==1||isok=='1'){
571 fetchData(2,start,city,cityid);
572 }
573
574 //got to next date
575 //send result to server
576 if(isok==2||isok=='2')
577 {
578 //send job
579 _HP.methods.general.generateJson(_HP.hotels.Booking,arg.job,arg.id,start,slave).then(answer=>{
580
581 //next cityid
582 if(idate==countdate){
583 hp_bot.job.updateOldJob(start,cityid,arg.dates);
584 nextCityId()
585 }
586
587 //next cityid
588 if(idate<countdate){
589 //Go to Nekt Date
590 console.log("=> going to next date: "+idate);
591 hp_bot.job.updateOldJob(start,null,null);
592 nextDate(cityid)
593 }
594
595 })
596 }
597
598 //Get new IP Address
599 if(isok==0||isok=='0'){
600 //standart IP: 192.168.56.1
601 hp_bot.general.getNewIP().then(gotNewIP=>{
602 fetchData(adult,start,city,cityid);
603 })
604 }
605
606 })
607
608 }
609
610 },
611 "api" : async function startApiJob() {}
612 },
613 general:{
614 "generateDeviceID": function generateDeviceID(show = false) {
615 //
616
617 let nr1 = Math.floor((Math.random() * 9999) + 1000);
618 let nr2 = Math.floor((Math.random() * 9999) + 1000);
619 let nr3 = Math.floor((Math.random() * 1) + 10);
620 if(show)
621 console.log('generateDeviceID: '+ `72331403-${nr1}-${nr2}-${nr3}`)
622
623 return `72331403-${nr1}-${nr2}-${nr3}`
624 },
625 "getNewIP": function getNewIP(ischanging=false) {
626
627 return new Promise(resolve=>{
628
629 console.log('old ip: ' + currentip);
630
631 hp_bot.general.generateDeviceID(true);
632
633 _CONTROLLER.tor.newTorSession(async function(err){
634 if(err)console.log(err)
635 currentip = await _CONTROLLER.showExternalIP(false);
636 console.log("new ip:"+currentip);
637 setTimeout(()=>{
638 resolve(currentip)
639 },5000)
640
641 })
642
643 /*
644 if(ischanging)
645 socket.emit('newip',slave, async function(status) {
646 console.log("callback")
647 currentip = await _CONTROLLER.showExternalIP(false)
648
649 if(!currentip)
650 setTimeout(()=>{getNewIP();},1000);
651 else
652 {
653 console.log("new ip:"+currentip)
654 resolve(currentip)
655 }
656 });
657
658 else{
659 setTimeout(()=>{
660 resolve(currentip)
661 },40000)
662 }
663 */
664 })
665 },
666 "getXMLData": function fetchIphoneXMLData(adult,start,end,city,cityid) {
667 return new Promise(resolve=>{
668 const devid = hp_bot.general.generateDeviceID();
669 const _url = `https://iphone-xml.booking.com/json/bookings.getHotelAvailabilityMobile?detail_level=1&genius_freebies=2&show_distance_cc=1&request_less_fields=2&show_facilities_review_score=1&show_last_minute_saving=1&include_translations=2&add_bh_info=1&languagecode=de&urgency=1&include_taxes=1&include_count=1&location_source=autocomplete&show_price_trend=1&show_deals=flash%2Clastm%2Csmart%2Cgenius&children_qty=0&low_av_alternatives=0&&&order_by=popularity&show_if_class_is_estimated=1&show_categories=v3&show_location_score=1&show_last_reservation_text=1&show_reinforcement_text=v2&show_filtered_facilities=1&show_if_no_cc_allowed=2&show_soldout=1&show_is_personalized_result=0&show_flash_saving=1&max_persons=-2&use_direct_payment=1&add_ctrip_info=1&include_rack_rate_savings=1&&include_url=1&bh_alt_urgency_msg=1&show_refundable=1&check_price_is_final=1&show_extra_charges=1&room_qty=1&show_if_city_center=1&available_rooms=0&price_buckets=100&flash_deals_amount_if_unsigned=2&show_low_availability=2&show_business_badge=1&autoextend=1&user_os=6.0.1&user_version=11.3.1-android&network_type=wifi&bt=1&arrival_date=${start}&departure_date=${end}&city_ids=${cityid.replace('city=', '').replace('|si=ai%2Cco%2Cci%2Cre%2Cdi', '')}&rows=5000&device_id=${devid}da-ef8edab1ad7f&guest_qty=${adult}`;
670 const options = {
671 headers: {
672 'Content-Type' : 'application/x-www-form-urlencoded',
673 'Authorization': 'Basic dGhlc2FpbnRzYnY6ZGdDVnlhcXZCeGdN'
674 },
675 url: _url,
676 timeout: 900000
677 };
678
679 //Send request
680 _CONTROLLER.tor.request(options, function (err, resp, body) {
681 if(body)
682 resolve(JSON.parse(body));
683 else
684 console.log('no data')
685 })
686 })
687 },
688 "validateXmlResult": function (xml,arg,adult,start,_city,cityid) {
689
690 console.log("check validate");
691
692 let xmlcity = null;
693
694 //remove space and + at start and at the end
695 const cityname = arg.name.trim().replace('/+/g','');
696 const listcity = [];
697
698 try
699 {
700
701 let found = false;
702 xmlcity = xml.filter;
703
704 if(xmlcity)
705 xmlcity.forEach(fitler=>
706 {
707 if(fitler.id == 'city')
708 {
709 if(fitler.categories)
710 fitler.categories.forEach(categorycity=>{
711 listcity.push(categorycity.name);
712 if(categorycity.name.toUpperCase().indexOf(cityname.toUpperCase())>=0)
713 found = categorycity;
714 })
715 }
716 });
717
718 if(found==false && listcity.length>1)
719 {
720 let message = {'warrning':'city_ids did not match cityname '+cityname,
721 'list':listcity}
722
723 hp_bot.general.errorHandler(arg,adult,start,cityname,cityid,message,'Mobile Parser',true);
724 return null;
725 }
726 else
727 {
728 console.log("Data is valid")
729 return true;
730 }
731
732 }
733 catch (TypeError)
734 {
735 console.log(" error validate date")
736 console.log(xmlcity)
737 console.log(TypeError)
738 return true;
739 }
740
741 },
742 "errorHandler": function (arg,adult,start,city,cityid,message,type='mobile',rebootanddelete=false) {
743
744 console.log( "errorHandler " + type)
745 city =city.replace('/+/g','');
746
747 const hosturl='http://'+hostip+':'+hostport+'/errorjob';
748 let log = {
749 "job" : JSON.stringify(arg),
750 "jobid" : arg.job,
751 "message": JSON.stringify(message) + ',{ "cityid":'+cityid+',"city":"'+city+'" }',
752 "cityid" : cityid,
753 "city" : city,
754 "slave" : slave,
755 "client" : client,
756 };
757
758 _CONTROLLER.request.post({
759 headers: {'content-type' : 'application/x-www-form-urlencoded'},
760 url: hosturl,
761 json: true,
762 form: {
763 data: JSON.stringify(log)
764 }
765 },
766 function(error, response, body){
767
768 //Remove backup if error is not acceptable
769 //example if cityid is incorrect
770 if(rebootanddelete){
771 // _CONTROLLER.exec('rm /var/node/backup.json');
772 }
773 });
774 setTimeout(()=>{
775 if(rebootanddelete){}
776 // _CONTROLLER.reboot();
777 else
778 _CONTROLLER.version();
779 },10000)
780
781 }
782 },
783};
784
785//EXPORT Functions
786hp_bot.constructor.prototype.start = hp_bot.init;
787hp_bot.constructor.prototype.getNewJob = hp_bot.job.getNewJob;
788module.exports = hp_bot.constructor;
789
790
791//###Error Output
792process.on('uncaughtException', function (error) {
793 console.log(error.stack);
794});
795
796// Add a connect listener
797socket.on('connect', function (socket) {
798 console.log('Connected!');
799});
800socket.on('disconnect', function(){
801 console.log("lost connection to server")
802 socket.connect()
803 socket.emit('slaves',slave)
804 hp_bot.init();
805 // reconnect
806});