stealth_browser_automation 0.0.7

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bcd01c1ba08e79360a113829c62e85d65ae8f414
4
+ data.tar.gz: 4701e9a79e13ab2111d83f8c48a722eab6245f37
5
+ SHA512:
6
+ metadata.gz: 63de89c480ebcd7aa799f6a6fd32b7a8759d29454929f8941a74601bdac2b6be35356240ecd48fb1edf712e0a8281921ad9e1339794cf2628bfdca7ee9b82c9c
7
+ data.tar.gz: 27d27a9e215ae62df3eacfa9175ad756f73ed1c77e33ca8b236caf7cd458c7549df14fb96823d7914b17bd737b4c5c2e13b090f1a3b85647fba06404f767f76e
data/lib/baseproxy.rb ADDED
@@ -0,0 +1,33 @@
1
+ module BlackStack
2
+
3
+ module BaseProxy
4
+
5
+ #
6
+ def chrome_switches
7
+ return ["--proxy-server=#{self.ip}:#{self.port}","--proxy-user-and-password=#{self.user}:#{self.password}"]
8
+ end
9
+
10
+ # Warning: Out to Date
11
+ def firefox_profile_parameter(agent_name = nil, profile_name = nil)
12
+ if (profile_name == nil)
13
+ profile = Selenium::WebDriver::Firefox::Profile.new
14
+ else
15
+ profile = Selenium::WebDriver::Firefox::Profile.from_name profile_name
16
+ end
17
+
18
+ if (agent_name!=nil)
19
+ profile['general.useragent.override'] = agent_name
20
+ end
21
+
22
+ proxy = Selenium::WebDriver::Proxy.new(:http => self.ip.to_s+":"+self.port.to_s, :ssl => self.ip.to_s+":"+self.port.to_s)
23
+ profile.proxy = proxy
24
+ return profile
25
+ end
26
+
27
+ # Warning: Out to Date
28
+ def phantomjs_switches
29
+ return ['--proxy='+self.ip.to_s+':'+self.port.to_s, '--proxy-auth='+self.user.to_s+':'+self.password.to_s, '--ignore-ssl-errors=yes', '--ssl-protocol=any', '--load-images=false']
30
+ end
31
+ end
32
+
33
+ end # module BlackStack
@@ -0,0 +1,498 @@
1
+ require 'watir-webdriver'
2
+ require "pathname"
3
+
4
+ module BlackStack
5
+
6
+ class PampaBrowser < Watir::Browser
7
+
8
+ attr_accessor :proxy, :lnuser, :agent_name, :profile_name
9
+
10
+ def notify(method)
11
+ url = "#{BlackStack::Pampa::api_protocol}://#{PROCESS.ws_url}:#{PROCESS.ws_port}/api1.3/pampa/browser/notify.json"
12
+ res = BlackStack::Netting::call_post(url, {
13
+ :api_key => BlackStack::Pampa::api_key,
14
+ :filename => $0,
15
+ :method => method,
16
+ :worker_name => PROCESS.fullWorkerName,
17
+ :id_proxy => self.proxy.nil? ? nil : self.proxy.id,
18
+ :id_lnuser => self.lnuser.nil? ? nil : self.lnuser.id,
19
+ :worker_assigned_process => PROCESS.worker.assigned_process,
20
+ :profile_name => self.profile_name,
21
+ :agent_name => self.agent_name,
22
+ })
23
+ parsed = JSON.parse(res.body)
24
+ if parsed['status'] != "success"
25
+ raise "Error Tracing BrowserActivity: #{parsed['status']}"
26
+ end
27
+ end
28
+
29
+ def push_screenshot(filename=nil)
30
+ filename = "#{PROCESS.fullWorkerName}.png" if filename.nil?
31
+ BrowserFactory::screenshot(filename)
32
+
33
+ image = MiniMagick::Image.open(filename)
34
+ w = (image[:width].to_f * 0.25).to_i
35
+ h = (image[:height].to_f * 0.25).to_i
36
+ image.resize "#{w.to_s}x#{h.to_s}"
37
+ image.format "png"
38
+ image.write filename
39
+
40
+ url = "#{BlackStack::Pampa::api_protocol}://#{PROCESS.ws_url}:#{PROCESS.ws_port}/api1.3/pampa/browser/screenshot.json"
41
+ res = RestClient::Request.execute(
42
+ :api_key => BlackStack::Pampa::api_key,
43
+ :verify_ssl => false,
44
+ :url => url,
45
+ :method => :post,
46
+ :headers => {
47
+ :accept => 'application/json',
48
+ :params => {
49
+ :api_key => BlackStack::Pampa.api_key,
50
+ :filename => filename,
51
+ },
52
+ },
53
+ :payload => {
54
+ :file => File.new(filename, "r"),
55
+ :multipart => false,
56
+ }
57
+ )
58
+ parsed = JSON.parse(res.body)
59
+ if parsed['status'] != "success"
60
+ raise "Error Uploading Screenshot: #{parsed['status']}"
61
+ end
62
+ end
63
+
64
+ def back
65
+ self.notify("back")
66
+ super
67
+ self.push_screenshot
68
+ end
69
+
70
+ def forward
71
+ self.notify("forward")
72
+ super
73
+ self.push_screenshot
74
+ end
75
+
76
+ def execute_script(script, *args)
77
+ self.notify("execute_script")
78
+ super
79
+ self.push_screenshot
80
+ end
81
+
82
+ def goto(url)
83
+ self.notify("goto")
84
+ super
85
+ self.push_screenshot
86
+ end
87
+
88
+ def screenshot
89
+ self.notify("screenshot")
90
+ super
91
+ end
92
+
93
+ def reset!
94
+ self.notify("reset!")
95
+ super
96
+ end
97
+
98
+ def refresh
99
+ self.notify("refresh")
100
+ super
101
+ self.push_screenshot
102
+ end
103
+
104
+ def inspect
105
+ self.notify("inspect")
106
+ super
107
+ end
108
+
109
+ =begin # se da de baja, porque incremente el # de llamadas a la API de 800/hora a 5000/hora, y eso requiere mas infraestructura
110
+ def send_keys(*args)
111
+ self.notify("send_keys")
112
+ super
113
+ end
114
+ =end
115
+
116
+ def text
117
+ self.notify("text")
118
+ super
119
+ end
120
+
121
+ def title
122
+ self.notify("title")
123
+ super
124
+ end
125
+
126
+ def url
127
+ self.notify("url")
128
+ super
129
+ end
130
+
131
+ def wait(timeout = 5)
132
+ self.notify("wait")
133
+ super
134
+ end
135
+ end # class PampaBrowser
136
+
137
+
138
+ class BrowserFactory
139
+ LOCKING_FILENAME = BlackStack::MyProcess.macaddress # manejo de concurrencia en la creación de browsers
140
+ PROFILE_PID_LIST_FILENAME = "./browserfactory.%PRFILE_NAME%.list" # manejo de concurrencia en la creación de browsers
141
+ PROFILE_PID_LOCK_FILENAME = "./browserfactory.%PRFILE_NAME%.lock" # manejo de concurrencia en la creación de browsers
142
+ CHROME_EXTENSION_DIRECTORY = File.absolute_path('./chrome_extension')
143
+ DEFAULT_LOAD_TIMEOUT = 360
144
+ DEFAULT_CHROMEDRIVER_PATH = 'chromedriver.exe'
145
+
146
+ def self.addPidToProfileList(profile_name, pid)
147
+ fname = PROFILE_PID_LIST_FILENAME.gsub('%PRFILE_NAME%', profile_name)
148
+ f = File.open(fname, 'a')
149
+ f.write("#{pid},")
150
+ f.close
151
+ end
152
+
153
+ def self.removePidToProfileList(profile_name, pid)
154
+ fname = PROFILE_PID_LIST_FILENAME.gsub('%PRFILE_NAME%', profile_name)
155
+ s = File.read(fname)
156
+ s = s.gsub("#{pid},", "")
157
+ f = File.open(fname, 'w')
158
+ f.write(s)
159
+ f.close
160
+ end
161
+
162
+ def self.readPidToProfileList(profile_name)
163
+ if (profile_name != nil)
164
+ fname = PROFILE_PID_LIST_FILENAME.gsub('%PRFILE_NAME%', profile_name)
165
+ s = File.read(fname)
166
+ return s.split(",")
167
+ else
168
+ return []
169
+ end
170
+ end
171
+
172
+ def self.updateProfileList()
173
+ addPidToProfileList(@@profile_name, @@pid)
174
+ PROCESS.list().select { |p| p[:ppid] == @@pid && p[:executablepath] =~ /chrome\.exe/ }.each { |p2|
175
+ addPidToProfileList(@@profile_name, p2[:pid])
176
+ }
177
+ end
178
+
179
+ TYPE_PHANTOMJS = 0 # discontinued!
180
+ TYPE_FIREFOX = 1
181
+ TYPE_CHROME = 2
182
+
183
+ @@fd = File.open(LOCKING_FILENAME,"w")
184
+ @@fd_profile = nil
185
+ @@driver = nil
186
+ @@browser = nil
187
+ @@pid = nil
188
+ @@profile_name = nil
189
+
190
+
191
+ def self.lockProfileList()
192
+ # @@fd_profile.flock(File::LOCK_EX)
193
+ end
194
+
195
+ def self.releaseProfileList()
196
+ # @@fd_profile.flock(File::LOCK_UN)
197
+ =begin
198
+ begin
199
+ @@fd_profile.close
200
+ rescue
201
+ end
202
+ @@fd_profile = nil
203
+ =end
204
+ end
205
+
206
+
207
+ # NOTA: esta lista debe estar permitida por LinkedIn. De caso contrario, aparecera el mensaje "Upgrad your browser"
208
+ # Se puede obtener una lista actualizada de este sitio: https://techblog.willshouse.com/2012/01/03/most-common-user-agents/
209
+ @@arAgents = [
210
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
211
+ "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
212
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0",
213
+ "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
214
+ "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0",
215
+ "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
216
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063",
217
+ ]
218
+
219
+ # toma una captura del area visible de la pagina
220
+ # mas informacion: https://stackoverflow.com/questions/25543651/screenshot-of-entire-webpage-using-selenium-webdriver-rc-rails
221
+ def self.screenshot(filename)
222
+ @@driver.save_screenshot(filename)
223
+ end
224
+
225
+ def self.agents()
226
+ return @@arAgents
227
+ end
228
+
229
+ def self.getPid()
230
+ return @@pid
231
+ end
232
+
233
+ def self.isCreated()
234
+ if (self.readPidToProfileList(@@profile_name).size>0)
235
+ return true
236
+ end
237
+ if (@@browser != nil)
238
+ return true
239
+ end
240
+
241
+ return false
242
+ end
243
+
244
+ def self.isCreated?
245
+ self.isCreated()
246
+ end
247
+
248
+ def self.reservation_id()
249
+ q = "SELECT TOP 1 reservation_id FROM browserfactory WHERE host_name='#{Socket.gethostname}'"
250
+ row = DB[q].first
251
+ if (row == nil)
252
+ DB.execute("INSERT INTO browserfactory (reservation_id, reservation_time, host_name) VALUES (NULL, NULL, '#{Socket.gethostname}')")
253
+ end
254
+ row = DB[q].first
255
+ return row[:reservation_id]
256
+ end
257
+
258
+ def self.lock()
259
+ @@fd.flock(File::LOCK_EX)
260
+ end
261
+
262
+ def self.release()
263
+ @@fd.flock(File::LOCK_UN)
264
+ end
265
+
266
+ def self.destroy()
267
+ self.lockProfileList()
268
+ self.readPidToProfileList(@@profile_name).each { |pid|
269
+ MyProcess.kill(pid)
270
+ self.removePidToProfileList(@@profile_name, pid)
271
+ }
272
+ self.releaseProfileList()
273
+ begin
274
+ if (@@browser != nil)
275
+ @@browser.close
276
+ else
277
+ end
278
+ rescue => e
279
+ #
280
+ end
281
+ # reseteo las variables
282
+ @@browser = nil
283
+ @@pid = nil
284
+ @@profile_name = nil
285
+ @@driver = nil
286
+ @@type = nil
287
+ end
288
+
289
+ #
290
+ def self.chrome_version
291
+ res = `reg query \"HKEY_CURRENT_USER\\Software\\Google\\Chrome\\BLBeacon\" /v version`.scan(/REG_SZ (.*)$/).first
292
+ return nil if res.nil?
293
+ return res[0] if res.size > 0
294
+ return nil
295
+ end
296
+
297
+ #
298
+ def self.chromedriver_version(filename=nil)
299
+ res = `chromedriver -v`.scan(/ChromeDriver\s(.*)\s\(/).first if filename.nil?
300
+ res = `#{filename} -v`.scan(/ChromeDriver\s(.*)\s\(/).first if !filename.nil?
301
+ return nil if res.nil?
302
+ return res[0] if res.size > 0
303
+ return nil
304
+ end
305
+
306
+ #
307
+ def self.chrome(h)
308
+ # TODO: check if h is a hash
309
+ # TODO: check the variable type of each param
310
+ # TODO: check mandatory params
311
+ self.launch_chrome(
312
+ h[:proxy],
313
+ h[:load_timeout].nil? ? DEFAULT_LOAD_TIMEOUT : h[:load_timeout],
314
+ h[:user_agent],
315
+ h[:profile_name],
316
+ h[:chromedriver_path],
317
+ h[:chromedriver_signature]
318
+ )
319
+ end
320
+
321
+ def self.mimic(mimic_profile_id)
322
+ begin
323
+ # Levantar el flag de reserva a mi favor
324
+ self.lock()
325
+
326
+ @@profile_name = mimic_profile_id
327
+ url_2 = nil
328
+ i = 0
329
+ max = 2
330
+ while (i<max && url_2.nil?)
331
+ i += 1
332
+ url = "http://127.0.0.1:#{BlackStack::StealthBrowserAutomation::Multilogin::mla_local_port}/api/v1/profile/start?automation=true&profileId=#{@@profile_name}"
333
+ uri = URI.parse(url)
334
+ req = Net::HTTP::Get.new(url)
335
+ res = Net::HTTP.start(uri.host, uri.port, :use_ssl => true, :verify_mode => OpenSSL::SSL::VERIFY_NONE) {|http|
336
+ http.request(req)
337
+ }
338
+ res = JSON.parse(res.body)
339
+ if !res.has_key?('status')
340
+ if i<max
341
+ self.release() # libero el flag de reserva de turno para crar un browser
342
+ raise "Error removing Multilogin profile: #{res.to_s}"
343
+ end
344
+ elsif res['status'] != 'OK'
345
+ if i<max
346
+ self.release() # libero el flag de reserva de turno para crar un browser
347
+ raise "Error removing Multilogin profile: #{res['status'].to_s}"
348
+ end
349
+ else
350
+ url_2 = res['value']
351
+ end
352
+ end
353
+
354
+ if !url_2.nil?
355
+ #uri_2 = URI.parse(url_2)
356
+ #req_2 = Net::HTTP::Get.new(url_2)
357
+ #res_2 = Net::HTTP.start(uri_2.host, uri_2.port, :use_ssl => true, :verify_mode => OpenSSL::SSL::VERIFY_NONE) {|http|
358
+ # http.request(req_2)
359
+ #}
360
+ @@driver = Selenium::WebDriver.for(:remote, :url => url_2, :desired_capabilities => nil)
361
+ bridge = @@driver.instance_variable_get(:@bridge)
362
+ service = bridge.instance_variable_get(:@service)
363
+ process = service.instance_variable_get(:@process)
364
+ @@pid = process.pid.to_s # es el PID del chromedriver
365
+ @@driver.manage.window().maximize()
366
+ @@browser = Watir::Browser.new(@@driver)
367
+ self.updateProfileList
368
+ end
369
+
370
+ rescue => e
371
+ p1 = PROCESS.list().select { |p| p[:ppid] == PROCESS.pid && p[:executablepath] =~ /chromedriver(.*)\.exe/ }.first
372
+ if (p1 != nil)
373
+ @@pid = p1[:pid]
374
+ self.updateProfileList
375
+ end
376
+ self.releaseProfileList()
377
+ raise e
378
+ end
379
+
380
+ #
381
+ self.releaseProfileList()
382
+
383
+ # libero el flag de reserva de turno para crar un browser
384
+ self.release()
385
+ end
386
+
387
+ #
388
+ def self.launch_chrome(proxy=nil, load_timeout=DEFAULT_LOAD_TIMEOUT, user_agent=nil, profile_name=nil, chromedriver_path=nil, chromedriver_signature=nil)
389
+ #
390
+ @@browser = nil
391
+ @@driver = nil
392
+ @@profile_name = profile_name
393
+
394
+ #
395
+ fname = PROFILE_PID_LOCK_FILENAME.gsub('%PRFILE_NAME%', @@profile_name)
396
+ @@fd_profile = File.open(fname,"w")
397
+
398
+ #
399
+ agent = @@arAgents[0] if user_agent.nil?
400
+ agent = user_agent if !user_agent.nil?
401
+
402
+ # #959 - valido que el parametro agents sea un array - porque si se pasa un string se lo procesa como array y se toma un UA name de un char.
403
+ #raise "Array expected in parameter agents in BrowserFactory.create." if !(agent.is_a? String)
404
+
405
+ #
406
+ while (@@browser == nil)
407
+
408
+ # Levantar el flag de reserva a mi favor
409
+ self.lock()
410
+
411
+ begin
412
+ client = Selenium::WebDriver::Remote::Http::Default.new
413
+ begin
414
+ client.read_timeout = load_timeout # for newest selenium webdriver version
415
+ rescue => e
416
+ client.timeout = load_timeout # deprecated in newest selenium webdriver version
417
+ end
418
+
419
+ # se agregan extensiones por el issue #1171
420
+ #
421
+ # UPDATE: Las extensiones en JavaScript para eliminar rastros, dejan rastros en si mismas.
422
+ #
423
+ switches = ["lang=en", "--log-level=3", "--user-agent=#{agent}", "disable-infobars", "load-extension=#{File.absolute_path('../chrome_extension')}"]
424
+
425
+ #
426
+ if (profile_name!=nil)
427
+
428
+ # issue #964
429
+ filename = "#{profile_name}/#{profile_name}/Default/Preferences"
430
+ if ( File.file?(filename) == true )
431
+ File.delete(filename)
432
+ end
433
+
434
+ # issue #964
435
+ filename = "#{profile_name}/#{profile_name}/Local State"
436
+ if ( File.file?(filename) == true )
437
+ File.delete(filename)
438
+ end
439
+
440
+ # configuro el browser para que use este perfil
441
+ switches += ["--user-data-dir=#{profile_name}/#{profile_name}"]
442
+
443
+ end
444
+
445
+ if (proxy!=nil)
446
+ switches += proxy.chrome_switches
447
+ end
448
+
449
+ self.lockProfileList()
450
+
451
+ begin
452
+
453
+ Selenium::WebDriver::Chrome.driver_path = chromedriver_path if !chromedriver_path.nil?
454
+ Selenium::WebDriver::Chrome.driver_path = DEFAULT_CHROMEDRIVER_PATH if chromedriver_path.nil?
455
+
456
+ @@driver = Selenium::WebDriver.for :chrome, :switches => switches
457
+ bridge = @@driver.instance_variable_get(:@bridge)
458
+ service = bridge.instance_variable_get(:@service)
459
+ process = service.instance_variable_get(:@process)
460
+ @@pid = process.pid.to_s # es el PID del chromedriver
461
+ @@driver.manage.window().maximize()
462
+ @@browser = PampaBrowser.new(@@driver)
463
+ @@browser.proxy = proxy.clone if !proxy.nil?
464
+ @@browser.agent_name = agent
465
+ @@browser.profile_name = profile_name
466
+ self.updateProfileList
467
+ rescue => e
468
+ p1 = PROCESS.list().select { |p| p[:ppid] == PROCESS.pid && p[:executablepath] =~ /chromedriver(.*)\.exe/ }.first
469
+ if (p1 != nil)
470
+ @@pid = p1[:pid]
471
+ self.updateProfileList
472
+ end
473
+ self.releaseProfileList()
474
+ raise e
475
+ end
476
+ self.releaseProfileList()
477
+
478
+ # Liberar el flag de reserva de creacion del browser
479
+ self.release()
480
+
481
+ #
482
+ rescue => e # TODO: Se atrapa una expecion porque sigue ocurreiendo el error reportado en el issue #134 y #129
483
+ # Liberar el flag de reserva de creacion del browser
484
+ self.release()
485
+
486
+ # disparar la exepcion
487
+ raise e
488
+ end
489
+ end # while
490
+
491
+ #
492
+ @@browser
493
+ end # create
494
+
495
+
496
+ end # class
497
+
498
+ end # module BlackStack
data/lib/proxy.rb ADDED
@@ -0,0 +1,80 @@
1
+ require_relative './baseproxy'
2
+
3
+ module BlackStack
4
+
5
+ # proxy almacenado en la base de datos
6
+ class Proxy < Sequel::Model(:proxy)
7
+ include BaseProxy
8
+
9
+ Proxy.dataset = Proxy.dataset.disable_insert_output
10
+
11
+ def self.availablesWithStealth(
12
+ process,
13
+ discretion_seconds_to_wait_if_proxy_has_been_reserved_but_didnt_finish,
14
+ discretion_seconds_to_wait_if_proxy_has_finished_successfully,
15
+ discretion_seconds_to_wait_if_proxy_has_been_blocked,
16
+ discretion_seconds_to_wait_for_long_sleep,
17
+ discretion_stop_from,
18
+ discretion_stop_until
19
+ )
20
+
21
+ q =
22
+ "SELECT COUNT(*) c FROM dbo.fnProxyDiscretion(" +
23
+ "'#{process}', " +
24
+ "#{discretion_seconds_to_wait_if_proxy_has_been_reserved_but_didnt_finish.to_s}, " +
25
+ "#{discretion_seconds_to_wait_if_proxy_has_finished_successfully.to_s}, " +
26
+ "#{discretion_seconds_to_wait_if_proxy_has_been_blocked.to_s}, " +
27
+ "#{discretion_seconds_to_wait_for_long_sleep.to_s}, " +
28
+ "'#{discretion_stop_from.to_s}', " +
29
+ "'#{discretion_stop_until.to_s}')"
30
+
31
+ return DB[q].first[:c].to_i
32
+ end
33
+
34
+ def self.getWithStealth(
35
+ process,
36
+ discretion_seconds_to_wait_if_proxy_has_been_reserved_but_didnt_finish,
37
+ discretion_seconds_to_wait_if_proxy_has_finished_successfully,
38
+ discretion_seconds_to_wait_if_proxy_has_been_blocked,
39
+ discretion_seconds_to_wait_for_long_sleep,
40
+ discretion_stop_from,
41
+ discretion_stop_until
42
+ )
43
+ rid = guid()
44
+ DB.execute(
45
+ "EXEC dbo.reserveProxyWithDiscretion " +
46
+ "'#{rid}', " +
47
+ "'#{process}', " +
48
+ "#{discretion_seconds_to_wait_if_proxy_has_been_reserved_but_didnt_finish.to_s}, " +
49
+ "#{discretion_seconds_to_wait_if_proxy_has_finished_successfully.to_s}, " +
50
+ "#{discretion_seconds_to_wait_if_proxy_has_been_blocked.to_s}, " +
51
+ "#{discretion_seconds_to_wait_for_long_sleep.to_s}, " +
52
+ "'#{discretion_stop_from.to_s}', " +
53
+ "'#{discretion_stop_until.to_s}'"
54
+ )
55
+ BlackStack::Proxy.where(:reservation_id=>rid).first
56
+ end
57
+
58
+ def startJob()
59
+ DB.execute("UPDATE proxy SET reservation_start_time=GETDATE() WHERE [id]='#{self.id}'")
60
+ end
61
+
62
+ # TODO: Reemplazar el parametro records_before_long_sleep por un registro en la tabla PARAMS
63
+ def endJob(id_object, result, description, records_before_long_sleep)
64
+ # => #464
65
+ =begin
66
+ DB.execute("UPDATE proxy SET reservation_end_time=GETDATE(), reservation_result='#{result.to_s}', reservation_description='#{description.to_s}' WHERE [id]='#{self.id}'")
67
+
68
+ DB.execute(
69
+ "EXEC dbo.endProxyJob " +
70
+ "'#{self.id}', " +
71
+ "'#{id_object}', " +
72
+ "#{result.to_s}, " +
73
+ "'#{description.to_s}', " +
74
+ "#{records_before_long_sleep.to_s} "
75
+ )
76
+ =end
77
+ end
78
+ end
79
+
80
+ end # module BlackStack
@@ -0,0 +1,8 @@
1
+
2
+ module BlackStack
3
+
4
+ class ProxyProvider < Sequel::Model(:proxyprovider)
5
+ ProxyProvider.dataset = ProxyProvider.dataset.disable_insert_output
6
+ end
7
+
8
+ end # module BlackStack
@@ -0,0 +1,9 @@
1
+
2
+ module BlackStack
3
+
4
+ class RemoteProxy
5
+ attr_accessor :id, :ip, :port, :user, :password
6
+ include BaseProxy
7
+ end
8
+
9
+ end # module BlackStack
@@ -0,0 +1,73 @@
1
+ require 'mini_magick'
2
+ require 'rest_client'
3
+ require 'pampa_workers'
4
+
5
+ require_relative './baseproxy'
6
+ require_relative './remoteproxy'
7
+ require_relative './browserfactory'
8
+
9
+ module BlackStack
10
+
11
+ module StealthBrowserAutomation
12
+
13
+ module Multilogin
14
+ #
15
+ @@auth_token = nil
16
+ @@mla_version = nil
17
+ @@mla_local_port = nil
18
+
19
+ def self.auth_token()
20
+ @@auth_token
21
+ end
22
+
23
+ def self.mla_version()
24
+ @@mla_version
25
+ end
26
+
27
+ def self.mla_local_port()
28
+ @@mla_local_port
29
+ end
30
+
31
+ def self.set(h)
32
+ @@auth_token = h[:auth_token]
33
+ @@mla_version = h[:mla_version]
34
+ @@mla_local_port = h[:mla_local_port]
35
+ end
36
+
37
+ # returns the profileId of the new Mimic profile
38
+ def self.create_profile(data)
39
+ url = "https://api.multiloginapp.com/v2/profile?token=#{BlackStack::StealthBrowserAutomation::Multilogin::auth_token.to_s}&mlaVersion=#{BlackStack::StealthBrowserAutomation::Multilogin::mla_version.to_s}"
40
+ uri = URI(url)
41
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => true, :verify_mode => OpenSSL::SSL::VERIFY_NONE) do |http|
42
+ req = Net::HTTP::Post.new(uri)
43
+ req['Content-Type'] = 'application/json'
44
+ req.body = data.to_json
45
+ res = JSON.parse(http.request(req).body)
46
+ raise "Error creating Multilogin profile: #{res.to_s}" if !res.has_key?('uuid')
47
+ return res['uuid']
48
+ end
49
+ end
50
+
51
+ # returns the profileId of the new Mimic profile
52
+ def self.remove_profile(profile_id)
53
+ url = "https://api.multiloginapp.com/v1/profile/remove?token=#{BlackStack::StealthBrowserAutomation::Multilogin::auth_token.to_s}&profileId=#{profile_id}"
54
+ uri = URI.parse(url)
55
+ req = Net::HTTP::Get.new(url)
56
+ res = Net::HTTP.start(uri.host, uri.port, :use_ssl => true, :verify_mode => OpenSSL::SSL::VERIFY_NONE) {|http|
57
+ http.request(req)
58
+ }
59
+ res = JSON.parse(res.body)
60
+ raise "Error removing Multilogin profile: #{res.to_s}" if !res.has_key?('status')
61
+ raise "Error removing Multilogin profile: #{res['status'].to_s}" if res['status'] != 'OK'
62
+ end # def self.delete_profile
63
+ end # module MultiLogin
64
+
65
+ #
66
+ def self.require_db_classes()
67
+ # You have to load all the Sinatra classes after connect the database.
68
+ require_relative '../lib/proxy.rb'
69
+ end
70
+
71
+ end # module StealthBrowserAutomation
72
+
73
+ end # module BlackStack
metadata ADDED
@@ -0,0 +1,250 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stealth_browser_automation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ platform: ruby
6
+ authors:
7
+ - Leandro Daniel Sardi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mini_magick
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.9.3
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 4.9.3
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 4.9.3
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 4.9.3
33
+ - !ruby/object:Gem::Dependency
34
+ name: rest-client
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 2.0.2
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.0.2
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 2.0.2
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 2.0.2
53
+ - !ruby/object:Gem::Dependency
54
+ name: websocket
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 1.2.8
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.2.8
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 1.2.8
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.2.8
73
+ - !ruby/object:Gem::Dependency
74
+ name: json
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: 1.8.1
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.8.1
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.8.1
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 1.8.1
93
+ - !ruby/object:Gem::Dependency
94
+ name: tiny_tds
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: 1.0.5
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 1.0.5
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 1.0.5
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.0.5
113
+ - !ruby/object:Gem::Dependency
114
+ name: sequel
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: 4.28.0
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 4.28.0
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: 4.28.0
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 4.28.0
133
+ - !ruby/object:Gem::Dependency
134
+ name: blackstack_commons
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: 0.0.20
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: 0.0.20
143
+ type: :runtime
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: 0.0.20
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 0.0.20
153
+ - !ruby/object:Gem::Dependency
154
+ name: simple_cloud_logging
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 1.1.16
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 1.1.16
163
+ type: :runtime
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: 1.1.16
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: 1.1.16
173
+ - !ruby/object:Gem::Dependency
174
+ name: simple_command_line_parser
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: 1.1.1
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: 1.1.1
183
+ type: :runtime
184
+ prerelease: false
185
+ version_requirements: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - "~>"
188
+ - !ruby/object:Gem::Version
189
+ version: 1.1.1
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: 1.1.1
193
+ - !ruby/object:Gem::Dependency
194
+ name: pampa_workers
195
+ requirement: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - "~>"
198
+ - !ruby/object:Gem::Version
199
+ version: 0.0.39
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: 0.0.39
203
+ type: :runtime
204
+ prerelease: false
205
+ version_requirements: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - "~>"
208
+ - !ruby/object:Gem::Version
209
+ version: 0.0.39
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: 0.0.39
213
+ description: 'THIS GEM IS STILL IN DEVELOPMENT STAGE. Find documentation here: https://github.com/leandrosardi/stealth_browser_automation.'
214
+ email: leandro.sardi@expandedventure.com
215
+ executables: []
216
+ extensions: []
217
+ extra_rdoc_files: []
218
+ files:
219
+ - lib/baseproxy.rb
220
+ - lib/browserfactory.rb
221
+ - lib/proxy.rb
222
+ - lib/proxyprovider.rb
223
+ - lib/remoteproxy.rb
224
+ - lib/stealth_browser_automation.rb
225
+ homepage: https://rubygems.org/gems/stealth_browser_automation
226
+ licenses:
227
+ - MIT
228
+ metadata: {}
229
+ post_install_message:
230
+ rdoc_options: []
231
+ require_paths:
232
+ - lib
233
+ required_ruby_version: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: '0'
238
+ required_rubygems_version: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - ">="
241
+ - !ruby/object:Gem::Version
242
+ version: '0'
243
+ requirements: []
244
+ rubyforge_project:
245
+ rubygems_version: 2.4.5.1
246
+ signing_key:
247
+ specification_version: 4
248
+ summary: THIS GEM IS STILL IN DEVELOPMENT STAGE. Scrape the web and automate social
249
+ accounts with not footprints. Track the browser acivity. Manage proxies stealthly.
250
+ test_files: []