robro 0.1.1 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e88a67da2ab2954835ceb48c4780e7c7b919221bf071010566a375cc2b46bf59
4
- data.tar.gz: bf448e4ec1218620392f80f3e0a68d5a735b7001ec53bedca348c69403572e43
3
+ metadata.gz: a1ed8475085bbc598b8d65eb0bd03913e6c1d898156bed192373c9dd4543e025
4
+ data.tar.gz: b233b37e22fff88c921a08a524a87515805de9327531ecdb8d636cf4ec33e8ea
5
5
  SHA512:
6
- metadata.gz: e13f9f6c6d1b2b1213dce31b946e4788c6de82844bf743e761cb7b30cf07608b0fcc25245efc0a523b6e6e5011cf400425e2c2993e70c744e89ec88a3e54dd48
7
- data.tar.gz: e73077cb7f59764e3355b294a5d44377d158026db6ecd36df0126eea3c87b3f31988300fc241b7d756c67858f2bd4c84a87e459f451968d8f0e4bd7fe853fae5
6
+ metadata.gz: e826a76fe9e7dce91249b2a0d5bd19495bebe6202e2ff55be5871b6acad9d8c8b3a4a160f03062b5a8b82f72f8c68ec4dfba3e8ce10b592f88fb316f9e0af003
7
+ data.tar.gz: b4de6981f340ebcc59bf0c709dde1546440a67e6a53137550a58f8d816e19ecac58cf0075da48029c66ec616036dae0e8a24fb626d8691560bd04aeb6732dc95
data/.gitignore CHANGED
@@ -8,4 +8,3 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  /Gemfile.lock
11
- /.byebug_history
@@ -0,0 +1,36 @@
1
+ module Robro
2
+ class Browser
3
+ module Helpers
4
+ def remove_elements(css:)
5
+ page.execute_script "document.querySelectorAll('#{css}').forEach(e => { e.remove() } )"
6
+ end
7
+
8
+ def click_through_overlay(element)
9
+ element_regexp = {
10
+ firefox: /^Element (<.*>) is not clickable at point \((\d+),(\d+)\) because another element (?<element><.*>) obscures it$/,
11
+ chrome: /^element click intercepted: Element (<.*>) is not clickable at point \((\d+), (\d+)\)\. Other element would receive the click: (?<element><.*>)/,
12
+ }
13
+ loop do
14
+ element.click(wait: 1)
15
+ break
16
+ rescue StandardError => e
17
+ m_element = element_regexp[Robro.browser.application].match e.message
18
+ unless m_element.nil?
19
+ m_id = /id=\"(?<id>[a-zA-Z0-9-]+)/.match m_element[:element]
20
+ unless m_id.nil?
21
+ overlay_element_id = m_id[:id]
22
+ Robro.logger.debug "Removing '#{overlay_element_id}' that obscures target element"
23
+ remove_elements css: "##{overlay_element_id}"
24
+ else
25
+ Robro.logger.error "Unable to find element ID that obscures target (#{element}) in: '#{m_id[:element]}'"
26
+ debugger
27
+ end
28
+ else
29
+ Robro.logger.error "Unable to find element that obscures target (#{element}) in: '#{e.message}'"
30
+ debugger
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
data/lib/robro/browser.rb CHANGED
@@ -2,32 +2,35 @@ require 'capybara'
2
2
  require 'capybara/dsl'
3
3
  require 'capybara-screenshot'
4
4
 
5
+ require_relative 'browser/helpers'
6
+
5
7
  module Robro
6
8
  class Browser
7
9
  attr_reader :application
8
10
 
9
11
  include Capybara::DSL
12
+ include Robro::Browser::Helpers
10
13
 
11
- def initialize(application)
14
+ def initialize(application, headless: false)
12
15
  @application = application.to_sym
13
- @headless = false # FIXME ENV['GUI'].nil?
16
+ @headless = headless
14
17
 
15
18
  Capybara.configure do |config|
16
19
  config.run_server = false
17
20
  config.default_max_wait_time = 5
18
21
  end
19
22
 
20
- # FIXME Register chrome_jack_headless
21
23
  require 'selenium-webdriver'
22
24
  Capybara.register_driver :chrome_jack do |app|
23
- options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
24
- opts.args << '--start-maximized'
25
- opts.args << '--disable-blink-features'
26
- opts.args << '--disable-blink-features=AutomationControlled'
27
- opts.args << '--excludeSwitches=enable-automation'
28
- opts.args << '--disable-gpu'
29
- end
30
- driver = Capybara::Selenium::Driver.new(app, browser: :chrome, capabilities: options)
25
+ options = ::Selenium::WebDriver::Chrome::Options.new
26
+ options.add_argument('--headless') if headless
27
+ options.add_argument('--start-maximized')
28
+ options.add_argument('--disable-blink-features')
29
+ options.add_argument('--disable-blink-features=AutomationControlled')
30
+ options.add_argument('--excludeSwitches=enable-automation')
31
+ options.add_argument('--disable-gpu')
32
+
33
+ driver = Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
31
34
  bridge = driver.browser.send(:bridge)
32
35
 
33
36
  path = '/session/:session_id/chromium/send_command'
@@ -104,15 +107,13 @@ module Robro
104
107
 
105
108
  driver_name = case @application
106
109
  when :firefox
107
- 'selenium'
110
+ @headless ? 'selenium_headless' : 'selenium'
108
111
  when :chrome
109
112
  'chrome_jack'
110
113
  else
111
114
  raise "Unsupported browser: '#{application}'"
112
115
  end
113
116
 
114
- driver_name += '_headless' if @headless
115
-
116
117
  driver_name.to_sym
117
118
  end
118
119
  end
@@ -0,0 +1,24 @@
1
+ require 'tty-cursor'
2
+
3
+ require 'thor'
4
+
5
+ module Robro
6
+ class CLI < Thor
7
+ module Helpers
8
+ def self.show_download_progress(changes, details)
9
+ if changes['filename']
10
+ puts "Downloading '#{details['filename']}' (size: #{details['size']})"
11
+ puts " progress: 0% rate: --- remaining time: ---"
12
+ elsif changes['progress']
13
+ print TTY::Cursor.prev_line
14
+ print TTY::Cursor.clear_line
15
+ puts " progress: #{details['progress']} rate: #{details['rate']} remaining time: #{details['remaining_time']}"
16
+ elsif changes['rate_average']
17
+ puts " rate average: #{details['rate_average']}"
18
+ elsif changes['message']
19
+ puts "Download message: #{changes['message']}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
data/lib/robro/cli.rb CHANGED
@@ -4,8 +4,6 @@ require 'robro/user_scripts'
4
4
  require 'thor'
5
5
  require 'json'
6
6
 
7
- require 'active_support/inflector'
8
-
9
7
  module Robro
10
8
  class CLI < Thor
11
9
  desc 'browse URL', 'Start browser at URL, drop a shell'
@@ -18,22 +16,19 @@ module Robro
18
16
 
19
17
  quit = false
20
18
  until quit
21
- UserScripts.all.each do |us_class|
22
- us = us_class.new
23
- next unless uri.host.start_with? *(us.supported_urls)
19
+ us = UserScripts.find_for uri: uri
24
20
 
25
- unless us.nil?
26
- puts "Commands for this URL: #{us.supported_url_commands}"
21
+ unless us.nil?
22
+ puts "Commands for this URL: #{us.supported_url_commands}"
27
23
 
28
- us.supported_url_commands.each do |command|
29
- define_singleton_method command do |*args|
30
- us.send(command, *args)
31
- end
24
+ us.supported_url_commands.each do |command|
25
+ define_singleton_method command do |*args|
26
+ us.send(command, *args)
32
27
  end
33
28
  end
34
29
  end
35
30
 
36
- byebug
31
+ debugger
37
32
  end
38
33
  end
39
34
 
@@ -54,22 +49,20 @@ module Robro
54
49
  end
55
50
  end
56
51
 
57
- UserScripts.all.each do |us_class|
58
- us = us_class.new
59
- us.supported_url_commands.each do |command|
60
- desc "#{command} URL", command.to_s
61
- method_option :browser, :type => :string, :description => 'Supported values: firefox or chromium', :default => 'chrome'
62
- define_method command do |*args|
63
- url = args.shift
64
- raise Thor::Error, 'URL argument is missing' if url.nil?
52
+ UserScripts.all_commands.each do |command|
53
+ desc "#{command} URL", command.to_s
54
+ method_option :browser, :type => :string, :description => 'Supported values: firefox or chromium', :default => 'firefox'
55
+ define_method command do |*args|
56
+ url = args.shift
57
+ raise Thor::Error, 'URL argument is missing' if url.nil?
65
58
 
66
- uri = validate_url(url)
59
+ uri = validate_url(url)
67
60
 
68
- Robro.browser = Browser.new options[:browser]
69
- Robro.browser.visit uri
61
+ Robro.browser = Browser.new options[:browser]
70
62
 
71
- puts JSON.pretty_generate(us.send(command, *args))
72
- end
63
+ result = UserScripts.execute command, uri
64
+
65
+ puts JSON.pretty_generate(result)
73
66
  end
74
67
  end
75
68
  end
@@ -0,0 +1,123 @@
1
+ require 'open3'
2
+
3
+ module Robro
4
+ class Download
5
+ class Error < StandardError; end
6
+
7
+ attr_reader :details
8
+
9
+ def initialize(url)
10
+ @url = url.sub /^http:/, 'https:'
11
+ Robro.logger.debug "Attempt to download: #{@url}"
12
+ end
13
+
14
+ FILTERS = [
15
+ #--2020-11-10 22:30:31-- https://kubuntu.org/wp-content/uploads/2020/10/6ac0/GroovyBannerv4-1024x273.png
16
+ /^--\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}-- http/,
17
+ #Resolving kubuntu.org (kubuntu.org)... 162.213.33.214
18
+ /^Resolving /,
19
+ #Connecting to kubuntu.org (kubuntu.org)|162.213.33.214|:443... connected.
20
+ /^Connecting /,
21
+ #Reusing existing connection to kubuntu.org:443.
22
+ /^Reusing /,
23
+ ]
24
+
25
+ def run(&block)
26
+ @details = {}
27
+
28
+ result = false
29
+
30
+ Open3.popen2e(
31
+ { "LANG" => 'C'},
32
+ 'wget', '--continue', '--content-disposition', '--progress=dot:mega', @url,
33
+ ) do |stdin, stdout_and_err, thread|
34
+ stdout_and_err.each do |line|
35
+ line.chomp!
36
+ next if line.empty?
37
+
38
+ data = extract_details line
39
+
40
+ unless data.nil?
41
+ data['filename'] = eval "\"#{data['filename']}\"" unless data['filename'].nil? # Handle escaped UTF-8 chars
42
+ @details.merge! data
43
+ block.call data, @details
44
+ else
45
+ puts "==='#{line}'===" unless filtred?(line)
46
+ end
47
+ end
48
+
49
+ exit_status = thread.value
50
+ @details.merge!({ exit_status: exit_status })
51
+
52
+ raise Error.new "Error while downloading: #{error}" unless error.nil?
53
+ end
54
+ end
55
+
56
+ def downloaded?
57
+ success?
58
+ end
59
+
60
+ def success?
61
+ error.nil?
62
+ end
63
+
64
+ def error
65
+ return "#{details['error_code']}: #{details['error_text']}" unless details['error_code'].nil?
66
+ return "#{details['error_text']}" unless details['error_text'].nil?
67
+ return "Command execution failed with exit code: #{details[:exit_status].exitstatus}" unless details[:exit_status].success?
68
+
69
+ nil
70
+ end
71
+
72
+ private
73
+
74
+ def filtred?(line)
75
+ FILTERS.each do |regexp|
76
+ match = regexp.match line
77
+ return true unless match.nil?
78
+ end
79
+
80
+ false
81
+ end
82
+
83
+ EXTRACTORS = [
84
+ /^Length: (?<size>\d+)/,
85
+ /^Saving to: '(?<filename>.*)'$/,
86
+ # 64512K ,,,,,,,. ........ ........ ........ ........ ........ 2% 1.26M 37m23s
87
+ # 67584K ........ ........ ........ ........ ........ ........ 2% 870K 42m29s
88
+ # 101376K ........ ........ ........ ........ ........ ........ 4% 4.70M 8m56s
89
+ # 104448K ........ ........ ........ ........ ........ ........ 4% 5.87M 8m41s
90
+ # 107520K ........ ........ ........ ........ ........ ........ 4% 6.17M 8m27s
91
+ #2472960K ........ ........ .. 100% 5.80M=5m57s
92
+ /^.* [,. ]{8} [,. ]{8} [,. ]{8} [,. ]{8} [,. ]{8} [,. ]{8} *(?<progress>\d+%) (?<rate>.*)[ =](?<remaining_time>.*)$/,
93
+ #2020-11-08 09:29:55 ERROR 410: Gone.
94
+ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} ERROR (?<error_code>\d+): (?<error_text>.*)$/,
95
+ # [ skipping 79872K ]
96
+ /^ +\[ skipping (?<skipping>.*) \]/,
97
+ #2020-11-10 22:25:48 (1.19 MB/s) - 'GroovyBannerv4-1024x273.png' saved [278174/278174]
98
+ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \((?<rate_average>.*)\) - '.*' saved/,
99
+ # The file is already fully retrieved; nothing to do.
100
+ /^ (?<message>The file is already fully retrieved; nothing to do\.)$/,
101
+ #HTTP request sent, awaiting response... 200 OK
102
+ /^HTTP request sent, awaiting response... (?<http_status_code>\d+) (?<http_status_text>.*)$/,
103
+ #wget: unable to resolve host address 'kuubuntu.org'
104
+ /^wget: (?<error_text>unable to resolve host address '.*')$/,
105
+ #Cannot write to 'kubuntu.iso' (Success).
106
+ /^(?<error_text>Cannot write to .*)$/,
107
+ ]
108
+
109
+ def extract_details(line)
110
+ result = {}
111
+ EXTRACTORS.each do |extractor|
112
+ result.merge! extract(extractor, line)
113
+ end
114
+ result == {} ? nil : result
115
+ end
116
+
117
+ def extract(regexp, line)
118
+ match = regexp.match line
119
+ return {} if match.nil?
120
+ match.named_captures
121
+ end
122
+ end
123
+ end
@@ -1,3 +1,5 @@
1
+ require 'active_support/inflector'
2
+
1
3
  module Robro
2
4
  module UserScripts
3
5
  def self.all
@@ -8,7 +10,33 @@ module Robro
8
10
  end
9
11
  end
10
12
 
13
+ def self.all_commands
14
+ user_scripts = UserScripts.all.map(&:new)
15
+ user_scripts.map(&:supported_url_commands).flatten.uniq
16
+ end
17
+
18
+ def self.find_for(uri:)
19
+ user_scripts = UserScripts.all.map(&:new)
20
+ user_scripts.find { |us| uri.host.start_with? *(us.supported_urls) }
21
+ end
22
+
23
+ def self.execute(command, uri, *args)
24
+ us = find_for uri: uri
25
+
26
+ raise "No user scripts found for URI: #{uri}" if us.nil?
27
+
28
+ Robro.logger.info "Opening URL: #{uri}"
29
+ Robro.browser.visit uri
30
+
31
+ Robro.logger.info "User script: '#{us.class}' will process commnand '#{command}'"
32
+ us.send(command, uri)
33
+ end
34
+
11
35
  class Base
36
+ def browser
37
+ Robro.browser
38
+ end
39
+
12
40
  def commands
13
41
  raise NotImplementedError
14
42
  end
data/lib/robro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Robro
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/robro.rb CHANGED
@@ -2,7 +2,7 @@ require "robro/browser"
2
2
  require "robro/version"
3
3
 
4
4
  require 'tty/logger'
5
- require 'byebug'
5
+ require 'debug'
6
6
 
7
7
  module Robro
8
8
  class Error < StandardError; end
@@ -12,6 +12,7 @@ module Robro
12
12
  config.handlers = [
13
13
  [:console, { output: $stderr, level: :debug }],
14
14
  ]
15
+ config.metadata = %i[date time]
15
16
  end
16
17
  end
17
18
 
data/robro.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
 
9
9
  spec.summary = %q{Robotized Browser}
10
10
  spec.description = %q{Automate tasks that require a _real_ browser}
11
- spec.homepage = "https://github.com/opus-codium/robro"
11
+ spec.homepage = "https://github.com/neomilium/robro"
12
12
  spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
13
13
 
14
14
  spec.metadata["homepage_uri"] = spec.homepage
@@ -29,8 +29,9 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency "capybara-screenshot"
30
30
  spec.add_dependency "selenium-webdriver"
31
31
  spec.add_dependency "thor"
32
+ spec.add_dependency "tty-cursor"
32
33
  spec.add_dependency "tty-logger"
33
- spec.add_development_dependency "byebug"
34
+ spec.add_development_dependency "debug"
34
35
  spec.add_development_dependency "bundler"
35
36
  spec.add_development_dependency "rake"
36
37
  spec.add_development_dependency "rspec"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: robro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Romuald Conty
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-12 00:00:00.000000000 Z
11
+ date: 2025-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: tty-cursor
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: tty-logger
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -95,7 +109,7 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: byebug
112
+ name: debug
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - ">="
@@ -169,17 +183,20 @@ files:
169
183
  - exe/robro
170
184
  - lib/robro.rb
171
185
  - lib/robro/browser.rb
186
+ - lib/robro/browser/helpers.rb
172
187
  - lib/robro/cli.rb
188
+ - lib/robro/cli/helpers.rb
189
+ - lib/robro/download.rb
173
190
  - lib/robro/user_scripts.rb
174
191
  - lib/robro/version.rb
175
192
  - robro.gemspec
176
- homepage: https://github.com/opus-codium/robro
193
+ homepage: https://github.com/neomilium/robro
177
194
  licenses: []
178
195
  metadata:
179
- homepage_uri: https://github.com/opus-codium/robro
180
- source_code_uri: https://github.com/opus-codium/robro.git
181
- changelog_uri: https://github.com/opus-codium/robro/Changelog.md
182
- post_install_message:
196
+ homepage_uri: https://github.com/neomilium/robro
197
+ source_code_uri: https://github.com/neomilium/robro.git
198
+ changelog_uri: https://github.com/neomilium/robro/Changelog.md
199
+ post_install_message:
183
200
  rdoc_options: []
184
201
  require_paths:
185
202
  - lib
@@ -194,8 +211,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
211
  - !ruby/object:Gem::Version
195
212
  version: '0'
196
213
  requirements: []
197
- rubygems_version: 3.1.2
198
- signing_key:
214
+ rubygems_version: 3.3.15
215
+ signing_key:
199
216
  specification_version: 4
200
217
  summary: Robotized Browser
201
218
  test_files: []