stealth_browser_automation 1.1.34 → 1.2.38
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 +4 -4
- data/lib/baseproxy.rb +25 -1
- data/lib/browserfactory.rb +560 -188
- data/lib/proxy.rb +16 -2
- data/lib/stealth_browser_automation.rb +332 -23
- metadata +66 -6
data/lib/proxy.rb
CHANGED
@@ -5,9 +5,23 @@ module BlackStack
|
|
5
5
|
# proxy almacenado en la base de datos
|
6
6
|
class Proxy < Sequel::Model(:proxy)
|
7
7
|
include BaseProxy
|
8
|
-
|
9
8
|
Proxy.dataset = Proxy.dataset.disable_insert_output
|
10
|
-
|
9
|
+
|
10
|
+
##
|
11
|
+
# creates new connection to google.com using +Faraday+ lib. Uses CGI::Cookie class
|
12
|
+
# to parse the cookie returned in the response. It then checks for the presense of
|
13
|
+
# "NID" cookie set by Google. If the cookie exists, proxy server is working just fine.
|
14
|
+
#
|
15
|
+
# reference: https://github.com/apoorvparijat/proxy-test
|
16
|
+
#
|
17
|
+
def test
|
18
|
+
if self.user.to_s.size > 0 && self.password.to_s.size > 0
|
19
|
+
return BlackStack::Proxy.test("#{self.ip}:#{self.port.to_s}")
|
20
|
+
else
|
21
|
+
return BlackStack::Proxy.test("#{self.user}:#{self.password}@#{self.ip}:#{self.port.to_s}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
11
25
|
# TODO: deprecated
|
12
26
|
def self.availablesWithStealth(
|
13
27
|
process,
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'faraday'
|
1
2
|
require 'mini_magick'
|
2
3
|
require 'rest_client'
|
3
4
|
require 'pampa_workers'
|
@@ -9,17 +10,198 @@ require_relative './browserfactory'
|
|
9
10
|
module BlackStack
|
10
11
|
|
11
12
|
module StealthBrowserAutomation
|
12
|
-
|
13
|
+
# TODO: Develop this!
|
14
|
+
=begin
|
15
|
+
module RQuery
|
16
|
+
|
17
|
+
# TODO: doc me
|
18
|
+
def self.set(h)
|
19
|
+
# TODO: code me
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create custom exceptions
|
23
|
+
# More information: https://www.honeybadger.io/blog/ruby-custom-exceptions/
|
24
|
+
class ElementNotFound < StandardError
|
25
|
+
# TODO: code me
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create custom exceptions
|
29
|
+
# More information: https://www.honeybadger.io/blog/ruby-custom-exceptions/
|
30
|
+
class UntilTimeout < StandardError
|
31
|
+
# TODO: code me
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create custom exceptions
|
35
|
+
# More information: https://www.honeybadger.io/blog/ruby-custom-exceptions/
|
36
|
+
class WhileTimeout < StandardError
|
37
|
+
# TODO: code me
|
38
|
+
end
|
39
|
+
|
40
|
+
# TODO: doc me
|
41
|
+
class ElementCollection
|
42
|
+
# array of watir elements
|
43
|
+
attr_accessor :elements
|
44
|
+
|
45
|
+
# TODO: doc me
|
46
|
+
def initialize()
|
47
|
+
self.elements = []
|
48
|
+
end
|
49
|
+
|
50
|
+
# TODO: doc me
|
51
|
+
def size()
|
52
|
+
# TODO: code me
|
53
|
+
end
|
54
|
+
|
55
|
+
# TODO: doc me
|
56
|
+
def count()
|
57
|
+
self.size
|
58
|
+
end
|
59
|
+
|
60
|
+
# TODO: doc me
|
61
|
+
def get(n)
|
62
|
+
# TODO: code me
|
63
|
+
end
|
64
|
+
|
65
|
+
# TODO: doc me
|
66
|
+
def first
|
67
|
+
# TODO: code me
|
68
|
+
end
|
69
|
+
|
70
|
+
# TODO: doc me
|
71
|
+
def last
|
72
|
+
# TODO: code me
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# TODO: doc me
|
77
|
+
class TextFieldCollection < BlackStack::StealthBrowserAutomation::RQuery::ElementCollection
|
78
|
+
# TODO: doc me
|
79
|
+
def get(n)
|
80
|
+
# TODO: code me
|
81
|
+
end
|
82
|
+
|
83
|
+
# TODO: doc me
|
84
|
+
def first
|
85
|
+
# TODO: code me
|
86
|
+
end
|
87
|
+
|
88
|
+
# TODO: doc me
|
89
|
+
def last
|
90
|
+
# TODO: code me
|
91
|
+
end
|
92
|
+
end # class TextFieldCollection
|
93
|
+
|
94
|
+
# TODO: doc me
|
95
|
+
class ButtonCollection < BlackStack::StealthBrowserAutomation::RQuery::ElementCollection
|
96
|
+
# TODO: doc me
|
97
|
+
def get(n)
|
98
|
+
# TODO: code me
|
99
|
+
end
|
100
|
+
|
101
|
+
# TODO: doc me
|
102
|
+
def first
|
103
|
+
# TODO: code me
|
104
|
+
end
|
105
|
+
|
106
|
+
# TODO: doc me
|
107
|
+
def last
|
108
|
+
# TODO: code me
|
109
|
+
end
|
110
|
+
end # class ButtonCollection
|
111
|
+
|
112
|
+
# TODO: doc me
|
113
|
+
class Element
|
114
|
+
|
115
|
+
# return true if there is one or more elemnts in the screenshot that match with the cutout
|
116
|
+
def exists?
|
117
|
+
# TODO: code me
|
118
|
+
end
|
119
|
+
|
120
|
+
# return the watir element
|
121
|
+
# raise ElementNotFound exception if the element does not exist.
|
122
|
+
def wc
|
123
|
+
# TODO: code me
|
124
|
+
end
|
125
|
+
|
126
|
+
end # class Element
|
127
|
+
|
128
|
+
# TODO: doc me
|
129
|
+
class TextField < BlackStack::StealthBrowserAutomation::RQuery::Element
|
130
|
+
# return the watir textfield
|
131
|
+
# raise ElementNotFound exception if the element does not exist.
|
132
|
+
def wc
|
133
|
+
# TODO: code me
|
134
|
+
end
|
135
|
+
end # class TextField
|
136
|
+
|
137
|
+
# TODO: doc me
|
138
|
+
class Button < BlackStack::StealthBrowserAutomation::RQuery::Element
|
139
|
+
# return the watir button
|
140
|
+
# raise ElementNotFound exception if the element does not exist.
|
141
|
+
def wc
|
142
|
+
# TODO: code me
|
143
|
+
end
|
144
|
+
end # class Button
|
145
|
+
|
146
|
+
|
147
|
+
class FixedMatching
|
148
|
+
attr_accessor :br
|
149
|
+
|
150
|
+
# TODO: doc me
|
151
|
+
def initialize(init_br)
|
152
|
+
self.br = init_br
|
153
|
+
end
|
154
|
+
|
155
|
+
# return a BlackStack::StealthBrowserAutomation::RQuery::TextField
|
156
|
+
def text_field(image_path, options={})
|
157
|
+
# TODO: code me
|
158
|
+
end
|
159
|
+
|
160
|
+
# return a BlackStack::StealthBrowserAutomation::RQuery::TextFields
|
161
|
+
def text_fields(image_path, options={})
|
162
|
+
# TODO: code me
|
163
|
+
end
|
164
|
+
|
165
|
+
# return a BlackStack::StealthBrowserAutomation::RQuery::Button
|
166
|
+
def button(image_path, options={})
|
167
|
+
# TODO: code me
|
168
|
+
end
|
169
|
+
|
170
|
+
# return a BlackStack::StealthBrowserAutomation::RQuery::Buttons
|
171
|
+
def buttons(image_path, options={})
|
172
|
+
# TODO: code me
|
173
|
+
end
|
174
|
+
|
175
|
+
# how to write a function that receive a block of code between {} ?
|
176
|
+
# more infromation: https://flylib.com/books/en/2.44.1/writing_a_method_that_accepts_a_block.html
|
177
|
+
#
|
178
|
+
# raise UntilTimeout exception if the condition is never true during the timeout period.
|
179
|
+
def until(timeout)
|
180
|
+
# TODO: code me
|
181
|
+
end
|
182
|
+
|
183
|
+
# how to write a function that receive a block of code between {} ?
|
184
|
+
# more infromation: https://flylib.com/books/en/2.44.1/writing_a_method_that_accepts_a_block.html
|
185
|
+
#
|
186
|
+
# raise WhileTimeout exception if the condition is never false during the timeout period.
|
187
|
+
def while(timeout)
|
188
|
+
# TODO: code me
|
189
|
+
end
|
190
|
+
|
191
|
+
end # class FixedMatching
|
192
|
+
|
193
|
+
end # module RQuery
|
194
|
+
=end
|
13
195
|
module Multilogin
|
14
|
-
|
15
|
-
|
196
|
+
VERSION_4 = '4.5.1'
|
197
|
+
VERSION_5 = '5'
|
198
|
+
ALLOWED_VERSIONS = [VERSION_4, VERSION_5]
|
199
|
+
|
200
|
+
#
|
16
201
|
@@mla_version = nil
|
17
202
|
@@mla_local_port = nil
|
18
|
-
|
19
|
-
|
20
|
-
@@auth_token
|
21
|
-
end
|
22
|
-
|
203
|
+
@@mla_app_properties_file = nil
|
204
|
+
|
23
205
|
def self.mla_version()
|
24
206
|
@@mla_version
|
25
207
|
end
|
@@ -29,11 +211,39 @@ module BlackStack
|
|
29
211
|
end
|
30
212
|
|
31
213
|
def self.set(h)
|
32
|
-
|
214
|
+
raise "Unknown mla_version. Allowed values are #{ALLOWED_VERSIONS.join(', ')}." if !ALLOWED_VERSIONS.include? h[:mla_version]
|
215
|
+
|
33
216
|
@@mla_version = h[:mla_version]
|
34
|
-
|
217
|
+
x = h[:mla_local_port]
|
218
|
+
port = x.is_a?(Array) ? x[rand(0..x.size-1)] : x
|
219
|
+
@@mla_local_port = port
|
220
|
+
@@mla_app_properties_file = h[:mla_app_properties_file]
|
35
221
|
end
|
36
|
-
|
222
|
+
|
223
|
+
def self.properties_hash()
|
224
|
+
filename = @@mla_app_properties_file
|
225
|
+
key = 'multiloginapp.auth_tkn'
|
226
|
+
h = {}
|
227
|
+
File.foreach(filename).map { |line|
|
228
|
+
a = line.split('=')
|
229
|
+
}.each { |a|
|
230
|
+
h[ a[0].gsub(/[^0-9a-z\.\_\- ]/i, '').strip ] = a[1].gsub(/[^0-9a-z\.\_\- ]/i, '').strip
|
231
|
+
}
|
232
|
+
h
|
233
|
+
end
|
234
|
+
|
235
|
+
def self.auth_token()
|
236
|
+
self.properties_hash['multiloginapp.auth_tkn']
|
237
|
+
end
|
238
|
+
|
239
|
+
# ---------------------------------------------------------------------------------
|
240
|
+
# ---------------------------------------------------------------------------------
|
241
|
+
# --- LOCAL PROFILES MANAGEMENT
|
242
|
+
# --- TODO: Enable this when develop support for Multilogin Local Profiles
|
243
|
+
# ---------------------------------------------------------------------------------
|
244
|
+
# ---------------------------------------------------------------------------------
|
245
|
+
# TODO: Develop this if we start working with local profiles at some point
|
246
|
+
=begin
|
37
247
|
# returns the profileId of the new Mimic profile
|
38
248
|
def self.create_local_profile(data)
|
39
249
|
url = "http://127.0.0.1:#{BlackStack::StealthBrowserAutomation::Multilogin::mla_local_port.to_s}/api/v2/localprofile/create"
|
@@ -84,33 +294,133 @@ module BlackStack
|
|
84
294
|
return nil if h.nil?
|
85
295
|
h['sid']
|
86
296
|
end # def self.delete_profile
|
297
|
+
=end
|
298
|
+
|
299
|
+
# ---------------------------------------------------------------------------------
|
300
|
+
# ---------------------------------------------------------------------------------
|
301
|
+
# --- CLOUD PROFILES MANAGEMENT
|
302
|
+
# ---
|
303
|
+
# ---------------------------------------------------------------------------------
|
304
|
+
# ---------------------------------------------------------------------------------
|
305
|
+
|
306
|
+
# migrate a profile from Multilogin-4 to Multilogin-5
|
307
|
+
# Multilogin 5 required for upgrading profiles. Therefore, MLS5 is required to run this method
|
308
|
+
def self.upgrade(profile_id)
|
309
|
+
url = nil
|
310
|
+
ssl = nil
|
311
|
+
|
312
|
+
if @@mla_version == BlackStack::StealthBrowserAutomation::Multilogin::VERSION_5
|
313
|
+
url = "http://localhost.multiloginapp.com:#{BlackStack::StealthBrowserAutomation::Multilogin::mla_local_port.to_s}/api/v2/profile/#{profile_id}/migrate"
|
314
|
+
ssl = false
|
315
|
+
else
|
316
|
+
raise 'Invalid Multilogin Version (Multilogin 5 required for upgrading profiles)'
|
317
|
+
end
|
318
|
+
|
319
|
+
uri = URI(url)
|
320
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => ssl, :verify_mode => OpenSSL::SSL::VERIFY_NONE) do |http|
|
321
|
+
req = Net::HTTP::Post.new(uri)
|
322
|
+
req['Content-Type'] = 'application/json'
|
323
|
+
if http.request(req).body
|
324
|
+
res = JSON.parse(http.request(req).body)
|
325
|
+
if res["status"] == 'ERROR' && res["message"].include?('Update')
|
326
|
+
Multilogin::update_profile(profile_id)
|
327
|
+
upgrade(profile_id)
|
328
|
+
elsif !res.has_key?('uuid')
|
329
|
+
if res.to_s =~ /Profile is already migrated/ # TODO: Do a proper processing of the response json
|
330
|
+
return
|
331
|
+
else
|
332
|
+
raise "Error migrating Multilogin profile: #{res.to_s}"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end # def self.delete_profile
|
338
|
+
|
339
|
+
#Updates the browser core to the latest version
|
340
|
+
# Multilogin 5 required for upgrading profiles. Therefore, MLS5 is required to run this method
|
341
|
+
def self.update_profile(profile_id)
|
342
|
+
url = nil
|
343
|
+
ssl = nil
|
344
|
+
|
345
|
+
if @@mla_version == BlackStack::StealthBrowserAutomation::Multilogin::VERSION_5
|
346
|
+
url = "http://localhost.multiloginapp.com:#{BlackStack::StealthBrowserAutomation::Multilogin::mla_local_port.to_s}/api/v2/profile/#{profile_id}/update"
|
347
|
+
ssl = false
|
348
|
+
else
|
349
|
+
raise 'Invalid Multilogin Version (Multilogin 5 required for upgrading profiles)'
|
350
|
+
end
|
351
|
+
|
352
|
+
uri = URI(url)
|
353
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => ssl, :verify_mode => OpenSSL::SSL::VERIFY_NONE) do |http|
|
354
|
+
req = Net::HTTP::Post.new(uri)
|
355
|
+
req['Content-Type'] = 'application/json'
|
356
|
+
if http.request(req).body
|
357
|
+
res = JSON.parse(http.request(req).body)
|
358
|
+
raise "Error updating Multilogin profile: #{res.to_s}" if !res.has_key?('uuid')
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
87
362
|
|
88
363
|
# returns the profileId of the new Mimic profile
|
89
364
|
def self.create_profile(data)
|
90
|
-
url =
|
365
|
+
url = nil
|
366
|
+
ssl = nil
|
367
|
+
|
368
|
+
if @@mla_version == BlackStack::StealthBrowserAutomation::Multilogin::VERSION_4
|
369
|
+
#puts
|
370
|
+
#puts '4.create'
|
371
|
+
url = "https://api.multiloginapp.com/v2/profile?token=#{BlackStack::StealthBrowserAutomation::Multilogin::auth_token.to_s}&mlaVersion=#{BlackStack::StealthBrowserAutomation::Multilogin::mla_version.to_s}"
|
372
|
+
#puts url
|
373
|
+
ssl = true
|
374
|
+
elsif @@mla_version == BlackStack::StealthBrowserAutomation::Multilogin::VERSION_5
|
375
|
+
#puts
|
376
|
+
#puts '5.create'
|
377
|
+
url = "http://localhost.multiloginapp.com:#{BlackStack::StealthBrowserAutomation::Multilogin::mla_local_port.to_s}/api/v2/profile"
|
378
|
+
ssl = false
|
379
|
+
else
|
380
|
+
raise 'Invalid Multilogin Version'
|
381
|
+
end
|
382
|
+
|
91
383
|
uri = URI(url)
|
92
|
-
Net::HTTP.start(uri.host, uri.port, :use_ssl =>
|
384
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => ssl, :verify_mode => OpenSSL::SSL::VERIFY_NONE) do |http|
|
93
385
|
req = Net::HTTP::Post.new(uri)
|
94
386
|
req['Content-Type'] = 'application/json'
|
95
387
|
req.body = data.to_json
|
96
388
|
res = JSON.parse(http.request(req).body)
|
97
389
|
raise "Error creating Multilogin profile: #{res.to_s}" if !res.has_key?('uuid')
|
98
390
|
return res['uuid']
|
99
|
-
end
|
391
|
+
end
|
100
392
|
end
|
101
393
|
|
102
394
|
# returns the profileId of the new Mimic profile
|
103
395
|
def self.remove_profile(profile_id)
|
104
|
-
url =
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
396
|
+
url = nil
|
397
|
+
ssl = nil
|
398
|
+
|
399
|
+
if @@mla_version == BlackStack::StealthBrowserAutomation::Multilogin::VERSION_4
|
400
|
+
#puts
|
401
|
+
#puts '4.remove'
|
402
|
+
url = "https://api.multiloginapp.com/v2/profile/#{profile_id}?token=#{BlackStack::StealthBrowserAutomation::Multilogin::auth_token.to_s}"
|
403
|
+
ssl = true
|
404
|
+
elsif @@mla_version == BlackStack::StealthBrowserAutomation::Multilogin::VERSION_5
|
405
|
+
#puts
|
406
|
+
#puts '5.remove'
|
407
|
+
url = "http://localhost.multiloginapp.com:#{BlackStack::StealthBrowserAutomation::Multilogin::mla_local_port.to_s}/api/v2/profile/#{profile_id}"
|
408
|
+
ssl = false
|
409
|
+
else
|
410
|
+
raise 'Invalid Multilogin Version'
|
411
|
+
end
|
412
|
+
|
413
|
+
uri = URI(url)
|
414
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => ssl, :verify_mode => OpenSSL::SSL::VERIFY_NONE) {|http|
|
415
|
+
req = Net::HTTP::Delete.new(uri)
|
416
|
+
req['accept'] = 'application/json'
|
417
|
+
res = http.request(req)
|
418
|
+
return
|
419
|
+
raise "Error removing Multilogin profile: #{res.to_s}" if !res.has_key?('status')
|
420
|
+
raise "Error removing Multilogin profile: #{res['status'].to_s}" if res['status'] != 'OK'
|
109
421
|
}
|
110
|
-
res = JSON.parse(res.body)
|
111
|
-
raise "Error removing Multilogin profile: #{res.to_s}" if !res.has_key?('status')
|
112
|
-
raise "Error removing Multilogin profile: #{res['status'].to_s}" if res['status'] != 'OK'
|
113
422
|
end # def self.delete_profile
|
423
|
+
|
114
424
|
end # module MultiLogin
|
115
425
|
|
116
426
|
#
|
@@ -118,7 +428,6 @@ module BlackStack
|
|
118
428
|
# You have to load all the Sinatra classes after connect the database.
|
119
429
|
require_relative '../lib/proxy.rb'
|
120
430
|
end
|
121
|
-
|
122
431
|
end # module StealthBrowserAutomation
|
123
432
|
|
124
433
|
end # module BlackStack
|
metadata
CHANGED
@@ -1,15 +1,75 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stealth_browser_automation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.38
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leandro Daniel Sardi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.9.2
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.9.2
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.9.2
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.9.2
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: watir-webdriver
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 0.9.9
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.9.9
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 0.9.9
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.9.9
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: selenium-webdriver
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 3.14.1
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 3.14.1
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.14.1
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 3.14.1
|
13
73
|
- !ruby/object:Gem::Dependency
|
14
74
|
name: mini_magick
|
15
75
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,20 +196,20 @@ dependencies:
|
|
136
196
|
requirements:
|
137
197
|
- - "~>"
|
138
198
|
- !ruby/object:Gem::Version
|
139
|
-
version: 1.1.
|
199
|
+
version: 1.1.24
|
140
200
|
- - ">="
|
141
201
|
- !ruby/object:Gem::Version
|
142
|
-
version: 1.1.
|
202
|
+
version: 1.1.24
|
143
203
|
type: :runtime
|
144
204
|
prerelease: false
|
145
205
|
version_requirements: !ruby/object:Gem::Requirement
|
146
206
|
requirements:
|
147
207
|
- - "~>"
|
148
208
|
- !ruby/object:Gem::Version
|
149
|
-
version: 1.1.
|
209
|
+
version: 1.1.24
|
150
210
|
- - ">="
|
151
211
|
- !ruby/object:Gem::Version
|
152
|
-
version: 1.1.
|
212
|
+
version: 1.1.24
|
153
213
|
description: 'THIS GEM IS STILL IN DEVELOPMENT STAGE. Find documentation here: https://github.com/leandrosardi/stealth_browser_automation.'
|
154
214
|
email: leandro.sardi@expandedventure.com
|
155
215
|
executables: []
|