gologin 1.0.31 → 1.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/gologin.js CHANGED
@@ -5,6 +5,7 @@ const fs = require('fs');
5
5
  const os = require('os');
6
6
  const util = require('util');
7
7
  const rimraf = util.promisify(require('rimraf'));
8
+ const { access, unlink, writeFile, readFile } = require('fs').promises;
8
9
  const exec = util.promisify(require('child_process').exec);
9
10
  const { spawn, execFile } = require('child_process');
10
11
  const FormData = require('form-data');
@@ -12,7 +13,7 @@ const ProxyAgent = require('simple-proxy-agent');
12
13
  const decompress = require('decompress');
13
14
  const decompressUnzip = require('decompress-unzip');
14
15
  const path = require('path');
15
- const shell = require('shelljs');
16
+ const zipdir = require('zip-dir');
16
17
 
17
18
  const BrowserChecker = require('./browser-checker');
18
19
  const { BrowserUserDataManager } = require('./browser-user-data-manager');
@@ -54,7 +55,7 @@ class GoLogin {
54
55
  this.tmpdir = options.tmpdir;
55
56
  if (!fs.existsSync(this.tmpdir)) {
56
57
  debug('making tmpdir', this.tmpdir);
57
- shell.mkdir('-p', this.tmpdir);
58
+ fs.mkdirSync(this.tmpdir, { recursive: true })
58
59
  }
59
60
  }
60
61
 
@@ -99,7 +100,7 @@ class GoLogin {
99
100
  }
100
101
 
101
102
  async profiles() {
102
- const profilesResponse = await requests.get(`${API_URL}/browser/`, {
103
+ const profilesResponse = await requests.get(`${API_URL}/browser/v2`, {
103
104
  headers: {
104
105
  'Authorization': `Bearer ${this.access_token}`,
105
106
  'User-Agent': 'gologin-api',
@@ -127,7 +128,7 @@ class GoLogin {
127
128
  throw new Error(`Gologin /browser/${id} response error ${profileResponse.statusCode} INVALID TOKEN OR PROFILE NOT FOUND`);
128
129
  }
129
130
 
130
- if(profileResponse.statusCode == 401){
131
+ if (profileResponse.statusCode === 401) {
131
132
  throw new Error("invalid token");
132
133
  }
133
134
 
@@ -135,77 +136,86 @@ class GoLogin {
135
136
  }
136
137
 
137
138
  async emptyProfile() {
138
- return fs.readFileSync(path.resolve(__dirname, 'gologin_zeroprofile.b64')).toString();
139
+ return readFile(path.resolve(__dirname, 'gologin_zeroprofile.b64')).then(res => res.toString());
139
140
  }
140
141
 
141
142
  async getProfileS3(s3path) {
143
+ if (!s3path) {
144
+ throw new Error('s3path not found');
145
+ }
146
+
142
147
  const token = this.access_token;
143
148
  debug('getProfileS3 token=', token, 'profile=', this.profile_id, 's3path=', s3path);
144
- if (s3path) { //загрузка профиля из публичного бакета s3 быстрее
145
- const s3url = `https://gprofiles.gologin.com/${s3path}`.replace(/\s+/mg, '+');
146
- debug('loading profile from public s3 bucket, url=', s3url);
147
- const profileResponse = await requests.get(s3url, {
148
- encoding: null
149
- });
150
- if (profileResponse.statusCode !== 200) {
151
- debug(`Gologin S3 BUCKET ${s3url} response error ${profileResponse.statusCode} - use empty`);
152
- return '';
153
- }
154
- return Buffer.from(profileResponse.body);
155
- }
156
149
 
157
- debug('old-way loading profile');
158
- const profileResponse = await requests.get(`${API_URL}/browser/${this.profile_id}/profile-s3`, {
159
- headers: {
160
- 'Authorization': `Bearer ${token}`,
161
- },
150
+ const s3url = `https://gprofiles.gologin.com/${s3path}`.replace(/\s+/mg, '+');
151
+ debug('loading profile from public s3 bucket, url=', s3url);
152
+ const profileResponse = await requests.get(s3url, {
162
153
  encoding: null
163
154
  });
155
+
164
156
  if (profileResponse.statusCode !== 200) {
165
- debug(`Gologin /browser/${this.profile_id} response error ${profileResponse.statusCode} - use empty`);
157
+ debug(`Gologin S3 BUCKET ${s3url} response error ${profileResponse.statusCode} - use empty`);
166
158
  return '';
167
159
  }
160
+
168
161
  return Buffer.from(profileResponse.body);
169
162
  }
170
163
 
171
- async postFile(fileName, fileBody) {
172
- debug('POSTING FILE', fileBody.length);
173
- const fd = new FormData();
174
- const boundary = fd.getBoundary();
175
- const body = Buffer.concat([
176
- Buffer.from('--'),
177
- Buffer.from(boundary),
178
- Buffer.from('\r\n'),
179
- Buffer.from(`Content-Disposition: form-data; name="profile"; filename="${fileName}"`),
180
- Buffer.from('\r\n'),
181
- Buffer.from('\r\n'),
182
- Buffer.from(fileBody),
183
- Buffer.from('\r\n'),
184
- Buffer.from('--'),
185
- Buffer.from(boundary),
186
- Buffer.from('--'),
187
- Buffer.from('\r\n')
188
- ]);
189
- await new Promise((resolve) => {
190
- let options = {
191
- method: 'PATCH',
192
- headers: {
193
- 'Authorization': `Bearer ${this.access_token}`,
194
- 'Content-Type': 'multipart/form-data; boundary=' + boundary,
195
- 'Content-Length': body.length
196
- },
197
- body: body,
198
- url: `${API_URL}/browser/${this.profile_id}/profile-s3`,
199
- };
200
- requests(_.merge(options, {}), () => {
201
- resolve();
202
- });
164
+ async postFile(fileName, fileBuff) {
165
+ debug('POSTING FILE', fileBuff.length);
166
+ debug('Getting signed URL for S3');
167
+ const apiUrl = `${API_URL}/browser/${this.profile_id}/storage-signature`;
168
+
169
+ const signedUrl = await requests.get(apiUrl, {
170
+ headers: {
171
+ Authorization: `Bearer ${this.access_token}`,
172
+ 'user-agent': 'gologin-api',
173
+ },
174
+ maxAttempts: 3,
175
+ retryDelay: 2000,
176
+ timeout: 10 * 1000,
177
+ fullResponse: false,
178
+ });
179
+
180
+ const [uploadedProfileUrl] = signedUrl.split('?');
181
+
182
+ console.log('Uploading profile by signed URL to S3');
183
+ const bodyBufferBiteLength = Buffer.byteLength(fileBuff);
184
+ console.log('BUFFER SIZE', bodyBufferBiteLength);
185
+
186
+ await requests.put(signedUrl, {
187
+ headers: {
188
+ 'Content-Type': 'application/zip',
189
+ 'Content-Length': bodyBufferBiteLength,
190
+ },
191
+ body: fileBuff,
192
+ maxBodyLength: Infinity,
193
+ maxContentLength: Infinity,
194
+ maxAttempts: 3,
195
+ retryDelay: 2000,
196
+ timeout: 30 * 1000,
197
+ fullResponse: false,
203
198
  });
199
+
200
+ const uploadedProfileMetadata = await requests.head(uploadedProfileUrl, {
201
+ maxAttempts: 3,
202
+ retryDelay: 2000,
203
+ timeout: 10 * 1000,
204
+ fullResponse: true,
205
+ });
206
+
207
+ const uploadedFileLength = +uploadedProfileMetadata.headers['content-length'];
208
+ if (uploadedFileLength !== bodyBufferBiteLength) {
209
+ console.log('Uploaded file is incorrect. Retry with China File size:', uploadedFileLength);
210
+ throw new Error('Uploaded file is incorrect. Retry with China File size: ' + uploadedFileLength);
211
+ }
212
+
213
+ console.log('Profile has been uploaded to S3 successfully');
204
214
  }
205
215
 
206
216
  async emptyProfileFolder() {
207
217
  debug('get emptyProfileFolder');
208
- const profile = fs.readFileSync(path.resolve(__dirname, 'gologin_zeroprofile.zip'));
218
+ const profile = await readFile(path.resolve(__dirname, 'gologin_zeroprofile.zip'));
209
219
  debug('emptyProfileFolder LENGTH ::', profile.length);
210
220
  return profile;
211
221
  }
@@ -246,9 +256,8 @@ class GoLogin {
246
256
  .then(() => {
247
257
  debug('extraction done');
248
258
  debug('create uid.json');
249
- fs.writeFileSync(path.join(extPath, 'uid.json'), JSON.stringify({uid: that.profile_id}, null, 2))
250
- debug('uid.json created', fs.readFileSync(path.join(extPath, 'uid.json')).toString())
251
- return extPath;
259
+ return writeFile(path.join(extPath, 'uid.json'), JSON.stringify({ uid: that.profile_id }, null, 2))
260
+ .then(() => extPath);
252
261
  })
253
262
  .catch(async (e) => {
254
263
  debug('orbita extension error', e);
@@ -299,19 +308,21 @@ class GoLogin {
299
308
  height: parseInt(screenHeight, 10),
300
309
  };
301
310
 
302
- if (!(local && fs.existsSync(this.profile_zip_path))) {
311
+ const profileZipExists = await access(this.profile_zip_path).then(() => true).catch(() => false);
312
+ if (!(local && profileZipExists)) {
303
313
  try {
304
314
  profile_folder = await this.getProfileS3(_.get(profile, 's3Path', ''));
305
315
  }
306
316
  catch (e) {
307
317
  debug('Cannot get profile - using empty', e);
308
318
  }
319
+
309
320
  debug('FILE READY', this.profile_zip_path);
310
321
  if (!profile_folder.length) {
311
322
  profile_folder = await this.emptyProfileFolder();
312
323
  }
313
-
314
- fs.writeFileSync(this.profile_zip_path, profile_folder);
324
+
325
+ await writeFile(this.profile_zip_path, profile_folder);
315
326
 
316
327
  debug('PROFILE LENGTH', profile_folder.length);
317
328
  } else {
@@ -323,20 +334,23 @@ class GoLogin {
323
334
  await this.extractProfile(profilePath, this.profile_zip_path);
324
335
  debug('extraction done');
325
336
 
326
- if (fs.existsSync(path.join(profilePath, 'SingletonLock'))) {
337
+ const singletonLockPath = path.join(profilePath, 'SingletonLock');
338
+ const singletonLockExists = await access(singletonLockPath).then(() => true).catch(() => false);
339
+ if (singletonLockExists) {
327
340
  debug('removing SingletonLock');
328
- fs.unlinkSync(path.join(profilePath, 'SingletonLock'));
341
+ await unlink(singletonLockPath);
329
342
  debug('SingletonLock removed');
330
343
  }
331
344
 
332
345
  const pref_file_name = path.join(profilePath, 'Default', 'Preferences');
333
346
  debug('reading', pref_file_name);
334
347
 
335
- if (!fs.existsSync(pref_file_name)) {
348
+ const prefFileExists = await access(pref_file_name).then(() => true).catch(() => false);
349
+ if (!prefFileExists) {
336
350
  debug('Preferences file not exists waiting', pref_file_name);
337
351
  }
338
352
 
339
- const preferences_raw = fs.readFileSync(pref_file_name);
353
+ const preferences_raw = await readFile(pref_file_name);
340
354
  let preferences = JSON.parse(preferences_raw.toString());
341
355
  let proxy = _.get(profile, 'proxy');
342
356
  let name = _.get(profile, 'name');
@@ -436,7 +450,11 @@ class GoLogin {
436
450
  await BrowserUserDataManager.composeFonts(families, profilePath, this.differentOs);
437
451
  }
438
452
 
439
- fs.writeFileSync(path.join(profilePath, 'Default', 'Preferences'), JSON.stringify(_.merge(preferences, {
453
+ const [languages] = this.language.split(';');
454
+ preferences.gologin.langHeader = gologin.language;
455
+ preferences.gologin.languages = languages;
456
+
457
+ await writeFile(path.join(profilePath, 'Default', 'Preferences'), JSON.stringify(_.merge(preferences, {
440
458
  gologin
441
459
  })));
442
460
 
@@ -449,20 +467,23 @@ class GoLogin {
449
467
  }
450
468
 
451
469
  async commitProfile() {
452
- const data = await this.getProfileDataToUpdate();
453
- debug('begin updating', data.length);
454
- if (!data.length) {
470
+ const dataBuff = await this.getProfileDataToUpdate();
471
+
472
+ debug('begin updating', dataBuff.length);
473
+ if (!dataBuff.length) {
455
474
  debug('WARN: profile zip data empty - SKIPPING PROFILE COMMIT');
456
475
 
457
476
  return;
458
477
  }
478
+
459
479
  try {
460
480
  debug('Patching profile');
461
- await this.postFile('profile', data);
481
+ await this.postFile('profile', dataBuff);
462
482
  }
463
483
  catch (e) {
464
484
  debug('CANNOT COMMIT PROFILE', e);
465
485
  }
486
+
466
487
  debug('COMMIT COMPLETED');
467
488
  }
468
489
 
@@ -482,16 +503,16 @@ class GoLogin {
482
503
 
483
504
  async checkPortAvailable(port) {
484
505
  debug('CHECKING PORT AVAILABLE', port);
506
+
485
507
  try {
486
508
  const { stdout, stderr } = await exec(`lsof -i:${port}`);
487
- if (
488
- stdout && stdout.match(/LISTEN/gmi)
489
- ) {
509
+ if (stdout && stdout.match(/LISTEN/gmi)) {
490
510
  debug(`PORT ${port} IS BUSY`)
491
511
  return false;
492
512
  }
493
- } catch (e) { }
513
+ } catch (e) {}
494
514
  debug(`PORT ${port} IS OPEN`);
515
+
495
516
  return true;
496
517
  }
497
518
 
@@ -507,7 +528,7 @@ class GoLogin {
507
528
 
508
529
  async getTimeZone(proxy) {
509
530
  debug('getting timeZone proxy=', proxy);
510
- if(this.timezone){
531
+ if (this.timezone) {
511
532
  debug('getTimeZone from options', this.timezone);
512
533
  this._tz = this.timezone;
513
534
  return this._tz.timezone;
@@ -604,7 +625,7 @@ class GoLogin {
604
625
 
605
626
  async spawnBrowser() {
606
627
  let remote_debugging_port = this.remote_debugging_port;
607
- if(!remote_debugging_port){
628
+ if (!remote_debugging_port) {
608
629
  remote_debugging_port = await this.getRandomPort();
609
630
  }
610
631
 
@@ -706,7 +727,9 @@ class GoLogin {
706
727
  if (this.is_stopping) {
707
728
  return true;
708
729
  }
709
- const is_posting = options.postings || false;
730
+ const is_posting = options.posting ||
731
+ options.postings || // backward compability
732
+ false;
710
733
 
711
734
  if (this.uploadCookiesToServer) {
712
735
  await this.uploadProfileCookiesToServer();
@@ -782,34 +805,31 @@ class GoLogin {
782
805
 
783
806
  async getProfileDataToUpdate() {
784
807
  const zipPath = path.join(this.tmpdir, `gologin_${this.profile_id}_upload.zip`);
785
- try {
786
- fs.unlinkSync(zipPath);
787
- }
788
- catch (e) {
808
+ const zipExists = await access(zipPath).then(() => true).catch(() => false);
809
+ if (zipExists) {
810
+ await unlink(zipPath);
789
811
  }
812
+
790
813
  await this.sanitizeProfile();
791
814
  debug('profile sanitized');
792
- await new Promise(resolve => {
793
- debug('begin zipping');
794
- execFile(`cd ${this.profilePath()}; /usr/bin/zip`, [
795
- `-r ${zipPath}`,
796
- '*'
797
- ], {
798
- shell: true
799
- }, () => {
800
- debug('zipping done');
801
- resolve();
802
- });
803
- });
804
- debug('PROFILE ZIP CREATED', this.profilePath(), zipPath);
805
- try {
806
- const data = fs.readFileSync(zipPath);
807
- return data;
808
- }
809
- catch (e) {
810
- debug('saveprofile error', e);
811
- return '';
812
- }
815
+
816
+ const profilePath = this.profilePath();
817
+ const fileBuff = await new Promise((resolve, reject) => zipdir(profilePath,
818
+ {
819
+ saveTo: zipPath,
820
+ filter: (path) => !/RunningChromeVersion/.test(path),
821
+ }, (err, buffer) => {
822
+ if (err) {
823
+ reject(err);
824
+ return;
825
+ }
826
+
827
+ resolve(buffer);
828
+ })
829
+ )
830
+
831
+ debug('PROFILE ZIP CREATED', profilePath, zipPath);
832
+ return fileBuff;
813
833
  }
814
834
 
815
835
  async profileExists() {
@@ -853,11 +873,11 @@ class GoLogin {
853
873
  const fingerprint = await this.getRandomFingerprint(options);
854
874
  debug("fingerprint=", fingerprint)
855
875
 
856
- if(fingerprint.statusCode == 500){
876
+ if (fingerprint.statusCode === 500) {
857
877
  throw new Error("no valid random fingerprint check os param");
858
878
  }
859
879
 
860
- if(fingerprint.statusCode == 401){
880
+ if (fingerprint.statusCode === 401) {
861
881
  throw new Error("invalid token");
862
882
  }
863
883
 
@@ -887,7 +907,7 @@ class GoLogin {
887
907
  let user_agent = options.navigator?.userAgent;
888
908
  let orig_user_agent = json.navigator.userAgent;
889
909
  Object.keys(options).map((e)=>{ json[e] = options[e] });
890
- if(user_agent=='random'){
910
+ if (user_agent === 'random') {
891
911
  json.navigator.userAgent = orig_user_agent;
892
912
  }
893
913
  // console.log('profileOptions', json);
@@ -900,11 +920,11 @@ class GoLogin {
900
920
  json,
901
921
  });
902
922
 
903
- if(response.body.statusCode==400){
923
+ if (response.body.statusCode === 400) {
904
924
  throw new Error(`gologin failed account creation with status code, ${data.statusCode} DATA ${JSON.stringify(response.body.message)}`);
905
925
  }
906
926
 
907
- if(response.body.statusCode==500){
927
+ if (response.body.statusCode === 500) {
908
928
  throw new Error(`gologin failed account creation with status code, ${data.statusCode}`);
909
929
  }
910
930
  debug(JSON.stringify(response.body));
@@ -1045,7 +1065,8 @@ class GoLogin {
1045
1065
 
1046
1066
  const ORBITA_BROWSER = this.executablePath || this.browserChecker.getOrbitaPath;
1047
1067
 
1048
- if(!fs.existsSync(ORBITA_BROWSER)){
1068
+ const orbitaBrowserExists = await access(ORBITA_BROWSER).then(() => true).catch(() => false);
1069
+ if (!orbitaBrowserExists) {
1049
1070
  throw new Error(`Orbita browser is not exists on path ${ORBITA_BROWSER}, check executablePath param`);
1050
1071
  }
1051
1072
 
@@ -1071,12 +1092,12 @@ class GoLogin {
1071
1092
  return this.stopRemote();
1072
1093
  }
1073
1094
 
1074
- await this.stopAndCommit({ posting: false }, false);
1095
+ await this.stopAndCommit({ posting: true }, false);
1075
1096
  }
1076
1097
 
1077
1098
  async stopLocal(options) {
1078
1099
  const opts = options || { posting: false };
1079
- await this.stopAndCommit(options, true);
1100
+ await this.stopAndCommit(opts, true);
1080
1101
  }
1081
1102
 
1082
1103
  async waitDebuggingUrl(delay_ms, try_count=0) {
@@ -1113,7 +1134,7 @@ class GoLogin {
1113
1134
  }
1114
1135
  });
1115
1136
 
1116
- if(profileResponse.statusCode == 401){
1137
+ if (profileResponse.statusCode === 401){
1117
1138
  throw new Error("invalid token");
1118
1139
  }
1119
1140
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gologin",
3
- "version": "1.0.31",
3
+ "version": "1.0.35",
4
4
  "description": "A high-level API to control Orbita browser over GoLogin API",
5
5
  "main": "./gologin.js",
6
6
  "repository": {
@@ -26,10 +26,10 @@
26
26
  "requestretry": "^4.1.0",
27
27
  "rimraf": "^3.0.2",
28
28
  "selenium-webdriver": "^4.0.0-alpha.7",
29
- "shelljs": "^0.8.4",
30
29
  "simple-proxy-agent": "^1.1.0",
31
30
  "sqlite": "^4.0.23",
32
- "sqlite3": "^5.0.2"
31
+ "sqlite3": "^5.0.2",
32
+ "zip-dir": "^2.0.0"
33
33
  },
34
34
  "bugs": {
35
35
  "url": "https://github.com/gologinapp/gologin/issues"
@@ -371,7 +371,7 @@ class GoLogin(object):
371
371
  return json.loads(requests.get(API_URL + '/browser/fingerprint?os=' + os_type, headers=self.headers()).content.decode('utf-8'))
372
372
 
373
373
  def profiles(self):
374
- return json.loads(requests.get(API_URL + '/browser/', headers=self.headers()).content.decode('utf-8'))
374
+ return json.loads(requests.get(API_URL + '/browser/v2', headers=self.headers()).content.decode('utf-8'))
375
375
 
376
376
  def create(self, options={}):
377
377
  profile_options = self.getRandomFingerprint(options)