gologin 1.0.46 → 1.0.49

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.
@@ -3,6 +3,7 @@ const os = require('os');
3
3
  const request = require('requestretry');
4
4
  const { rmdirSync, createWriteStream } = require('fs');
5
5
  const { access, readFile, writeFile, mkdir, readdir, copyFile } = require('fs').promises;
6
+ const crypto = require('crypto');
6
7
 
7
8
  const fontsCollection = require('./fonts');
8
9
 
@@ -12,6 +13,7 @@ const FONTS_DIR_NAME = 'fonts';
12
13
  const HOMEDIR = os.homedir();
13
14
  const BROWSER_PATH = path.join(HOMEDIR, '.gologin', 'browser');
14
15
  const OS_PLATFORM = process.platform;
16
+ const DEFAULT_ORBITA_EXTENSIONS_NAMES = ['Google Hangouts', 'Chromium PDF Viewer', 'CryptoTokenExtension', 'Web Store'];
15
17
 
16
18
  class BrowserUserDataManager {
17
19
  static downloadCookies({ profileId, ACCESS_TOKEN, API_BASE_URL }) {
@@ -114,7 +116,7 @@ class BrowserUserDataManager {
114
116
  await writeFile(path.join(profilePath, 'Default', 'fonts_config'), result);
115
117
  }
116
118
 
117
- static setExtPaths(settings = {}, profileExtensionsCheckRes = []) {
119
+ static setExtPathsAndRemoveDeleted(settings = {}, profileExtensionsCheckRes = []) {
118
120
  const formattedLocalExtArray = profileExtensionsCheckRes.map((el) => {
119
121
  const [extFolderName = ''] = el.split(path.sep).reverse();
120
122
  const [originalId] = extFolderName.split('@');
@@ -128,21 +130,60 @@ class BrowserUserDataManager {
128
130
  }
129
131
  }).filter(Boolean);
130
132
 
131
- if (!formattedLocalExtArray.length) {
132
- return;
133
- }
134
-
135
133
  const extensionsSettings = settings.extensions?.settings || {};
136
134
  const extensionsEntries = Object.entries(extensionsSettings);
137
135
 
138
136
  extensionsEntries.forEach((extensionObj) => {
139
- const [extensionsId] = extensionObj;
140
- const localExtObj = formattedLocalExtArray.find(el => el.originalId === extensionsId);
137
+ let [extensionId, currentExtSettings = {}] = extensionObj;
138
+ const extName = currentExtSettings.manifest?.name || '';
139
+ let extPath = currentExtSettings.path || '';
140
+ let originalId = '';
141
+
142
+ const isExtensionToBeDeleted = ['resources', 'passwords-ext', 'cookies-ext'].some(substring => extPath.includes(substring))
143
+ || DEFAULT_ORBITA_EXTENSIONS_NAMES.includes(extName);
144
+ if (isExtensionToBeDeleted) {
145
+ delete extensionsSettings[extensionId];
146
+ return;
147
+ }
148
+
149
+ if (os.platform() === 'win32') {
150
+ extPath = extPath.replace(/\//g, '\\');
151
+ } else {
152
+ extPath = extPath.replace(/\\/g, '/');
153
+ }
154
+ extensionsSettings[extensionId].path = extPath;
155
+
156
+ const isExtensionManageable = ['chrome-extensions', 'user-extensions'].some(substring => extPath.includes(substring));
157
+ if (isExtensionManageable) {
158
+ const [extFolderName] = extPath.split(path.sep).reverse();
159
+ [originalId] = extFolderName.split('@');
160
+ const isExtensionInProfileSettings = formattedLocalExtArray.find(el => el.path.includes(originalId));
161
+ if (!isExtensionInProfileSettings) {
162
+ delete extensionsSettings[extensionId];
163
+ return;
164
+ }
165
+
166
+ if (!currentExtSettings.manifest?.key) {
167
+ const hexEncodedPath = crypto.createHash('sha256').update(extPath).digest('hex');
168
+ const newId = hexEncodedPath.split('').slice(0, 32).map(symbol => extIdEncoding[symbol]).join('');
169
+ delete extensionsSettings[extensionId];
170
+
171
+ extensionsSettings[newId] = currentExtSettings;
172
+ extensionId = newId;
173
+ }
174
+ } else {
175
+ const splittedPath = extPath.split(path.sep);
176
+ if (splittedPath.length === 2) {
177
+ [originalId] = splittedPath
178
+ }
179
+ }
180
+
181
+ const localExtObj = originalId && formattedLocalExtArray.find(el => el.path.includes(originalId));
141
182
  if (!localExtObj) {
142
183
  return;
143
184
  }
144
185
 
145
- extensionsSettings[extensionsId].path = localExtObj?.path || '';
186
+ extensionsSettings[extensionId].path = localExtObj?.path || '';
146
187
  });
147
188
 
148
189
  return extensionsSettings;
@@ -202,6 +243,25 @@ class BrowserUserDataManager {
202
243
  }
203
244
  }
204
245
 
246
+ const extIdEncoding = {
247
+ 0: 'a',
248
+ 1: 'b',
249
+ 2: 'c',
250
+ 3: 'd',
251
+ 4: 'e',
252
+ 5: 'f',
253
+ 6: 'g',
254
+ 7: 'h',
255
+ 8: 'i',
256
+ 9: 'j',
257
+ a: 'k',
258
+ b: 'l',
259
+ c: 'm',
260
+ d: 'n',
261
+ e: 'o',
262
+ f: 'p',
263
+ };
264
+
205
265
  module.exports = {
206
266
  BrowserUserDataManager,
207
267
  }
@@ -249,6 +249,28 @@ class ExtensionsManager {
249
249
 
250
250
  return Promise.all(removeFoldersPromises);
251
251
  }
252
+
253
+ getExtensionsToInstall(extensionsFromPref, extensionsFromDB) {
254
+ if (!extensionsFromPref) {
255
+ return [];
256
+ }
257
+
258
+ const objectEntries = Object.entries(extensionsFromPref);
259
+ const extensionsInPref = objectEntries?.map(([_, settings]) => {
260
+ const [extFolderName] = settings.path.split(path.sep).reverse();
261
+ const [originalId] = extFolderName.split('@');
262
+ return originalId;
263
+ }) || [];
264
+
265
+ return extensionsFromDB.reduce((acc, extension) => {
266
+ const [extFolderName] = extension.split(path.sep).reverse();
267
+ const [originalId] = extFolderName.split('@');
268
+ if (!extensionsInPref.includes(originalId)) {
269
+ acc.push(extension);
270
+ }
271
+ return acc;
272
+ }, []);
273
+ }
252
274
  }
253
275
 
254
276
  const crxToZip = (buf) => {
package/gologin.js CHANGED
@@ -50,6 +50,7 @@ class GoLogin {
50
50
  this.writeCookesFromServer = options.writeCookesFromServer;
51
51
  this.remote_debugging_port = options.remote_debugging_port || 0;
52
52
  this.timezone = options.timezone;
53
+ this.extensionPathsToInstall = [];
53
54
 
54
55
  if (options.tmpdir) {
55
56
  this.tmpdir = options.tmpdir;
@@ -59,16 +60,16 @@ class GoLogin {
59
60
  }
60
61
  }
61
62
 
62
- this.cookiesFilePath = path.join(this.tmpdir, `gologin_profile_${this.profile_id}`, 'Default', 'Cookies');
63
+ this.cookiesFilePath = path.join(this.tmpdir, `gologin_profile_${this.profile_id}`, 'Default', 'Network', 'Cookies');
63
64
  this.profile_zip_path = path.join(this.tmpdir, `gologin_${this.profile_id}.zip`);
64
65
  debug('INIT GOLOGIN', this.profile_id);
65
66
  }
66
-
67
+
67
68
  async checkBrowser() { return this.browserChecker.checkBrowser(this.autoUpdateBrowser) }
68
69
 
69
70
  async setProfileId(profile_id) {
70
71
  this.profile_id = profile_id;
71
- this.cookiesFilePath = path.join(this.tmpdir, `gologin_profile_${this.profile_id}`, 'Default', 'Cookies');
72
+ this.cookiesFilePath = path.join(this.tmpdir, `gologin_profile_${this.profile_id}`, 'Default', 'Network', 'Cookies');
72
73
  this.profile_zip_path = path.join(this.tmpdir, `gologin_${this.profile_id}.zip`);
73
74
  }
74
75
 
@@ -124,15 +125,15 @@ class GoLogin {
124
125
  }
125
126
  })
126
127
  debug("profileResponse", profileResponse.statusCode, profileResponse.body);
127
-
128
-
128
+
129
+
129
130
  if (profileResponse.statusCode === 404) {
130
131
  throw new Error(JSON.parse(profileResponse.body).message);
131
- }
132
+ }
132
133
 
133
134
  if (profileResponse.statusCode === 403) {
134
135
  throw new Error(JSON.parse(profileResponse.body).message);
135
- }
136
+ }
136
137
 
137
138
  if (profileResponse.statusCode !== 200) {
138
139
  throw new Error(`Gologin /browser/${id} response error ${profileResponse.statusCode} INVALID TOKEN OR PROFILE NOT FOUND`);
@@ -140,7 +141,7 @@ class GoLogin {
140
141
 
141
142
  if (profileResponse.statusCode === 401) {
142
143
  throw new Error("invalid token");
143
- }
144
+ }
144
145
 
145
146
 
146
147
  return JSON.parse(profileResponse.body);
@@ -254,14 +255,30 @@ class GoLogin {
254
255
  if (_.get(preferences, 'isM1')) {
255
256
  preferences.is_m1 = _.get(preferences, 'isM1');
256
257
  }
257
-
258
+
259
+ if (_.get(preferences, 'os') == 'android'){
260
+ const devicePixelRatio = _.get(preferences, "devicePixelRatio");
261
+ const deviceScaleFactorCeil = Math.ceil(devicePixelRatio || 3.5);
262
+ let deviceScaleFactor = devicePixelRatio;
263
+ if (deviceScaleFactorCeil === devicePixelRatio) {
264
+ deviceScaleFactor += 0.00000001;
265
+ }
266
+
267
+ preferences.mobile = {
268
+ enable: true,
269
+ width: parseInt(this.resolution.width, 10),
270
+ height: parseInt(this.resolution.height, 10),
271
+ device_scale_factor: deviceScaleFactor,
272
+ }
273
+ }
274
+
258
275
  preferences.mediaDevices = {
259
276
  enable: preferences.mediaDevices.enableMasking,
260
277
  videoInputs: preferences.mediaDevices.videoInputs,
261
278
  audioInputs: preferences.mediaDevices.audioInputs,
262
279
  audioOutputs: preferences.mediaDevices.audioOutputs,
263
280
  }
264
-
281
+
265
282
  return preferences;
266
283
  }
267
284
 
@@ -289,7 +306,7 @@ class GoLogin {
289
306
  });
290
307
 
291
308
  debug('createBrowserExtension done');
292
- }
309
+ }
293
310
 
294
311
  extractProfile(path, zipfile) {
295
312
  debug(`extactProfile ${zipfile}, ${path}`);
@@ -358,7 +375,7 @@ class GoLogin {
358
375
  } catch(e) {
359
376
  console.trace(e);
360
377
  profile_folder = await this.emptyProfileFolder();
361
- await writeFile(this.profile_zip_path, profile_folder);
378
+ await writeFile(this.profile_zip_path, profile_folder);
362
379
  await this.extractProfile(profilePath, this.profile_zip_path);
363
380
  }
364
381
 
@@ -377,12 +394,12 @@ class GoLogin {
377
394
  if (!prefFileExists) {
378
395
  debug('Preferences file not exists waiting', pref_file_name, '. Using empty profile');
379
396
  profile_folder = await this.emptyProfileFolder();
380
- await writeFile(this.profile_zip_path, profile_folder);
397
+ await writeFile(this.profile_zip_path, profile_folder);
381
398
  await this.extractProfile(profilePath, this.profile_zip_path);
382
399
  }
383
400
 
384
401
  const preferences_raw = await readFile(pref_file_name);
385
- let preferences = JSON.parse(preferences_raw.toString());
402
+ let preferences = JSON.parse(preferences_raw.toString());
386
403
  let proxy = _.get(profile, 'proxy');
387
404
  let name = _.get(profile, 'name');
388
405
  const chromeExtensions = _.get(profile, 'chromeExtensions');
@@ -406,13 +423,16 @@ class GoLogin {
406
423
  }
407
424
 
408
425
  let extSettings;
409
- if (ExtensionsManagerInst.useLocalExtStorage && profileExtensionsCheckRes.length) {
410
- extSettings = BrowserUserDataManager.setExtPaths(preferences, profileExtensionsCheckRes);
411
- } else if (!ExtensionsManagerInst.useLocalExtStorage) {
426
+ if (ExtensionsManagerInst.useLocalExtStorage) {
427
+ extSettings = BrowserUserDataManager.setExtPathsAndRemoveDeleted(preferences, profileExtensionsCheckRes);
428
+ } else {
412
429
  const originalExtensionsFolder = path.join(profilePath, 'Default', 'Extensions');
413
430
  extSettings = await BrowserUserDataManager.setOriginalExtPaths(preferences, originalExtensionsFolder);
414
431
  }
415
432
 
433
+ this.extensionPathsToInstall =
434
+ ExtensionsManagerInst.getExtensionsToInstall(extSettings, profileExtensionsCheckRes);
435
+
416
436
  if (extSettings) {
417
437
  const currentExtSettings = preferences.extensions || {};
418
438
  currentExtSettings.settings = extSettings
@@ -433,7 +453,7 @@ class GoLogin {
433
453
  'username': _.get(profile, 'autoProxyUsername'),
434
454
  'password': _.get(profile, 'autoProxyPassword'),
435
455
  }
436
-
456
+
437
457
  profile.proxy.username = _.get(profile, 'autoProxyUsername');
438
458
  profile.proxy.password = _.get(profile, 'autoProxyPassword');
439
459
  }
@@ -470,7 +490,7 @@ class GoLogin {
470
490
  publicIP: _.get(profile, 'webRTC.fillBasedOnIp') ? this._tz.ip : _.get(profile, 'webRTC.publicIp'),
471
491
  localIps: _.get(profile, 'webRTC.localIps', []),
472
492
  };
473
-
493
+
474
494
  debug('profile.webRtc=', profile.webRtc);
475
495
  debug('profile.timezone=', profile.timezone);
476
496
  debug('profile.mediaDevices=', profile.mediaDevices);
@@ -526,11 +546,11 @@ class GoLogin {
526
546
  }
527
547
 
528
548
  const [languages] = this.language.split(';');
529
-
549
+
530
550
  if(preferences.gologin==null){
531
551
  preferences.gologin = {};
532
552
  }
533
-
553
+
534
554
  preferences.gologin.langHeader = gologin.language;
535
555
  preferences.gologin.languages = languages;
536
556
  // debug("convertedPreferences=", preferences.gologin)
@@ -685,7 +705,7 @@ class GoLogin {
685
705
 
686
706
  async spawnArguments() {
687
707
  const profile_path = this.profilePath();
688
-
708
+
689
709
  let proxy = this.proxy;
690
710
  proxy = `${proxy.mode}://${proxy.host}:${proxy.port}`;
691
711
 
@@ -715,10 +735,10 @@ class GoLogin {
715
735
  let remote_debugging_port = this.remote_debugging_port;
716
736
  if (!remote_debugging_port) {
717
737
  remote_debugging_port = await this.getRandomPort();
718
- }
719
-
738
+ }
739
+
720
740
  const profile_path = this.profilePath();
721
-
741
+
722
742
  let proxy = this.proxy;
723
743
  let proxy_host = '';
724
744
  if (proxy) {
@@ -727,7 +747,7 @@ class GoLogin {
727
747
  }
728
748
 
729
749
  this.port = remote_debugging_port;
730
-
750
+
731
751
  const ORBITA_BROWSER = this.executablePath || this.browserChecker.getOrbitaPath;
732
752
  console.log("ORBITA_BROWSER=", ORBITA_BROWSER)
733
753
  const env = {};
@@ -757,12 +777,28 @@ class GoLogin {
757
777
 
758
778
  let params = [
759
779
  `--remote-debugging-port=${remote_debugging_port}`,
760
- `--user-data-dir=${profile_path}`,
761
- `--password-store=basic`,
780
+ `--user-data-dir=${profile_path}`,
781
+ `--password-store=basic`,
762
782
  `--tz=${tz}`,
763
783
  `--lang=${browserLang}`,
764
784
  ];
765
785
 
786
+ if (this.extensionPathsToInstall.length) {
787
+ if (Array.isArray(this.extra_params) && this.extra_params.length) {
788
+ this.extra_params.forEach((param, index) => {
789
+ if (!param.includes('--load-extension=')) {
790
+ return;
791
+ }
792
+
793
+ const [_, extPathsString] = param.split('=');
794
+ const extPathsArray = extPathsString.split(',');
795
+ this.extensionPathsToInstall = [...this.extensionPathsToInstall, ...extPathsArray];
796
+ this.extra_params.splice(index, 1);
797
+ });
798
+ }
799
+ params.push(`--load-extension=${this.extensionPathsToInstall.join(',')}`);
800
+ }
801
+
766
802
  if (this.fontsMasking) {
767
803
  let arg = '--font-masking-mode=2';
768
804
  if (this.differentOs) {
@@ -788,13 +824,13 @@ class GoLogin {
788
824
  const child = execFile(ORBITA_BROWSER, params, {env});
789
825
  // const child = spawn(ORBITA_BROWSER, params, { env, shell: true });
790
826
  child.stdout.on('data', (data) => debug(data.toString()));
791
- debug('SPAWN CMD', ORBITA_BROWSER, params.join(" "));
827
+ debug('SPAWN CMD', ORBITA_BROWSER, params.join(" "));
792
828
  }
793
829
 
794
830
  debug('GETTING WS URL FROM BROWSER');
795
831
 
796
832
  let data = await requests.get(`http://127.0.0.1:${remote_debugging_port}/json/version`, {json: true});
797
-
833
+
798
834
  debug('WS IS', _.get(data, 'body.webSocketDebuggerUrl', ''))
799
835
  this.is_active = true;
800
836
 
@@ -837,7 +873,7 @@ class GoLogin {
837
873
 
838
874
  if (!local) {
839
875
  await rimraf(path.join(this.tmpdir, `gologin_${this.profile_id}.zip`));
840
- }
876
+ }
841
877
  debug(`PROFILE ${this.profile_id} STOPPED AND CLEAR`);
842
878
  return false;
843
879
  }
@@ -847,7 +883,7 @@ class GoLogin {
847
883
  if (!this.port) {
848
884
  throw new Error('Empty GoLogin port');
849
885
  }
850
- const ls = await spawn('fuser',
886
+ const ls = await spawn('fuser',
851
887
  [
852
888
  '-k TERM',
853
889
  `-n tcp ${this.port}`
@@ -882,7 +918,7 @@ class GoLogin {
882
918
  await Promise.all(remove_dirs.map(d => {
883
919
  const path_to_remove = `${that.profilePath()}${d}`
884
920
  return new Promise(resolve => {
885
- debug('DROPPING', path_to_remove);
921
+ debug('DROPPING', path_to_remove);
886
922
  rimraf(path_to_remove, { maxBusyTries: 100 }, (e) => {
887
923
  // debug('DROPPING RESULT', e);
888
924
  resolve();
@@ -934,7 +970,7 @@ class GoLogin {
934
970
  return false;
935
971
  }
936
972
  debug('profile is', profileResponse.body);
937
- return true;
973
+ return true;
938
974
  }
939
975
 
940
976
 
@@ -943,16 +979,16 @@ class GoLogin {
943
979
 
944
980
  if (options.os) {
945
981
  os = options.os;
946
- }
982
+ }
947
983
 
948
- let fingerprint = await requests.get(`https://api.gologin.com/browser/fingerprint?os=${os}`,{
984
+ let fingerprint = await requests.get(`${API_URL}/browser/fingerprint?os=${os}`,{
949
985
  headers: {
950
986
  'Authorization': `Bearer ${this.access_token}`,
951
987
  'User-Agent': 'gologin-api',
952
988
  }
953
- });
989
+ });
954
990
 
955
- return JSON.parse(fingerprint.body);
991
+ return JSON.parse(fingerprint.body);
956
992
  }
957
993
 
958
994
  async create(options) {
@@ -960,7 +996,7 @@ class GoLogin {
960
996
 
961
997
  const fingerprint = await this.getRandomFingerprint(options);
962
998
  debug("fingerprint=", fingerprint)
963
-
999
+
964
1000
  if (fingerprint.statusCode === 500) {
965
1001
  throw new Error("no valid random fingerprint check os param");
966
1002
  }
@@ -1032,7 +1068,7 @@ class GoLogin {
1032
1068
  async update(options) {
1033
1069
  this.profile_id = options.id;
1034
1070
  const profile = await this.getProfile();
1035
-
1071
+
1036
1072
  if (options.navigator) {
1037
1073
  Object.keys(options.navigator).map((e)=>{profile.navigator[e]=options.navigator[e]});
1038
1074
  }
@@ -1045,7 +1081,7 @@ class GoLogin {
1045
1081
  headers: {
1046
1082
  'Authorization': `Bearer ${this.access_token}`
1047
1083
  }
1048
- });
1084
+ });
1049
1085
  debug('response', JSON.stringify(response.body));
1050
1086
  return response.body
1051
1087
  }
@@ -1070,7 +1106,7 @@ class GoLogin {
1070
1106
  accuracy: profileGeolocationParams.accuracy,
1071
1107
  }
1072
1108
  };
1073
-
1109
+
1074
1110
  getViewPort() {
1075
1111
  return { ...this.resolution };
1076
1112
  };
@@ -1150,7 +1186,7 @@ class GoLogin {
1150
1186
  if (!this.executablePath) {
1151
1187
  await this.checkBrowser();
1152
1188
  }
1153
-
1189
+
1154
1190
  const ORBITA_BROWSER = this.executablePath || this.browserChecker.getOrbitaPath;
1155
1191
 
1156
1192
  const orbitaBrowserExists = await access(ORBITA_BROWSER).then(() => true).catch(() => false);
@@ -1222,7 +1258,7 @@ class GoLogin {
1222
1258
  return {'status': 'failure', 'code': profileResponse.statusCode};
1223
1259
  }
1224
1260
  */
1225
-
1261
+
1226
1262
  // if (profileResponse.body === 'ok') {
1227
1263
  const profile = await this.getProfile();
1228
1264
 
@@ -1231,7 +1267,7 @@ class GoLogin {
1231
1267
  'Authorization': `Bearer ${this.access_token}`
1232
1268
  }
1233
1269
  });
1234
-
1270
+
1235
1271
  debug('profileResponse', profileResponse.statusCode, profileResponse.body);
1236
1272
 
1237
1273
  if (profileResponse.statusCode === 401){
@@ -1262,7 +1298,7 @@ class GoLogin {
1262
1298
  let wsUrl = await this.waitDebuggingUrl(delay_ms);
1263
1299
  if(wsUrl!=''){
1264
1300
  return { 'status': 'success', wsUrl }
1265
- }
1301
+ }
1266
1302
 
1267
1303
  return { 'status': 'failure', 'message': profileResponse.body };
1268
1304
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gologin",
3
- "version": "1.0.46",
3
+ "version": "1.0.49",
4
4
  "description": "A high-level API to control Orbita browser over GoLogin API",
5
5
  "main": "./gologin.js",
6
6
  "repository": {