Class: Html2rss::CLI

Inherits:
Thor
  • Object
show all
Defined in:
lib/html2rss/cli.rb,
lib/html2rss/cli/render.rb,
lib/html2rss/cli/validate.rb,
lib/html2rss/cli/probe_view.rb

Overview

The Html2rss command line interface.

Defined Under Namespace

Modules: Render, Validate Classes: ProbeView

Constant Summary collapse

STRATEGY_OPTION_ENUM =

Supported CLI strategy names.

Html2rss::FeedPipeline::StrategyPlan.accepted_names.map(&:to_s).freeze
STRATEGY_OPTION_DESC =

CLI strategy option description text.

'Optional request strategy (defaults to auto; ' \
"auto tries #{Html2rss::FeedPipeline::AutoFallback::CHAIN.join(' -> ')})".freeze
RECON_FILE_DESC =

CLI --file description for inspect/recon candidate lists.

'Candidate list file (one URL or slug\turl per line)'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.exit_on_failure?Boolean

Returns whether Thor should exit on command failure.

Returns:

  • (Boolean)

    whether Thor should exit on command failure



28
29
30
# File 'lib/html2rss/cli.rb', line 28

def self.exit_on_failure?
  true
end

.feed_emit_optionsObject

Shared Thor output options for apply/scrape.



47
48
49
50
# File 'lib/html2rss/cli.rb', line 47

def self.feed_emit_options
  method_option :format, type: :string, enum: %w[rss jsonfeed], default: 'rss'
  method_option :explain, type: :boolean, desc: 'Print status JSON to stderr', default: false
end

.feed_transport_optionsObject

Shared Thor request/transport options for feed-producing commands.



39
40
41
42
43
44
# File 'lib/html2rss/cli.rb', line 39

def self.feed_transport_options
  method_option :strategy, type: :string, desc: STRATEGY_OPTION_DESC, enum: STRATEGY_OPTION_ENUM
  method_option :max_redirects, type: :numeric, desc: 'Max redirects to follow'
  method_option :max_requests, type: :numeric, desc: 'Max request budget'
  method_option :input, type: :string, desc: 'Local HTML file path'
end

.probe_target_optionsObject

Shared Thor options for inspect/recon URL targets.



33
34
35
36
# File 'lib/html2rss/cli.rb', line 33

def self.probe_target_options
  method_option :strategy, type: :string, desc: STRATEGY_OPTION_DESC, enum: STRATEGY_OPTION_ENUM
  method_option :file, type: :string, desc: RECON_FILE_DESC
end

Instance Method Details

#apply(source = nil, feed_name = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • source (String, nil) (defaults to: nil)

    YAML file path or '-'

  • feed_name (String, nil) (defaults to: nil)

    optional named feed



197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/html2rss/cli.rb', line 197

def apply(source = nil, feed_name = nil) # rubocop:disable Metrics/AbcSize
  config = if File.file?(source.to_s)
             Html2rss.config_from_yaml_file(source.to_s, feed_name)
           else
             raw_content, = read_config_input(source)
             Config.from_yaml(raw_content)
           end
  config[:params] = options[:params] || {}
  apply_runtime_request_overrides!(config)
  apply_local_file_input!(config, options[:input]) if options[:input]

  run_feed_command { Html2rss.apply(config) }
end

#capture(target = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • target (String, nil) (defaults to: nil)

    URL or local HTML file



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/html2rss/cli.rb', line 117

def capture(target = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
  strategy, local_file_path, url = prepare_auto_inputs(target, options[:input])
  topics = options[:topics]&.split(',')&.map(&:strip)

  result = Html2rss::Capture.build(
    url,
    strategy:,
    items_selector: options[:items_selector],
    topics:,
    title: options[:title],
    summary: options[:summary],
    force: options[:force],
    enhance: options[:enhance],
    limit: options[:limit]&.to_i,
    max_redirects: options[:max_redirects],
    max_requests: options[:max_requests],
    local_file_path:
  )

  if result.native_feed? && !options[:force]
    $stderr.puts "First-party RSS/Atom feed detected at #{result.native_feed}. Use --force to capture anyway." # rubocop:disable Style/StderrPuts
    exit(3)
  end

  explain_json!(capture_explain_payload(result)) if options[:explain]
  handle_capture_output(result, url)
end

#doctor(type = 'botasaurus') ⇒ void

This method returns an undefined value.

Parameters:

  • type (String, nil) (defaults to: 'botasaurus')

    check type (default: botasaurus)



259
260
261
262
263
264
265
266
267
268
# File 'lib/html2rss/cli.rb', line 259

def doctor(type = 'botasaurus')
  case type.to_s
  when 'botasaurus'
    result = Html2rss::Doctor::Botasaurus.call(sample_url: options[:sample])
    explain_json!(result.to_h)
    raise Thor::Error, result.message unless result.ok
  else
    raise Thor::Error, "Unknown doctor type: #{type}. Supported: botasaurus"
  end
end

#inspect(target = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • target (String, nil) (defaults to: nil)

    URL, file, or '-' for stdin



59
60
61
62
63
64
65
66
67
68
# File 'lib/html2rss/cli.rb', line 59

def inspect(target = nil)
  with_recon_targets(target) do |urls, batch_mode|
    results = if batch_mode
                Html2rss.batch_inspect(urls, strategy: current_strategy).results
              else
                [Html2rss.inspect(urls.first, strategy: current_strategy, deep: options[:deep]).to_wire_h]
              end
    Render.inspect_output(results, format: options[:format], batch_mode:)
  end
end

#mcpvoid

This method returns an undefined value.



251
252
253
# File 'lib/html2rss/cli.rb', line 251

def mcp
  Html2rss::MCP.start(transport: options[:transport].to_sym, port: options[:port])
end

#recon(target = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • target (String, nil) (defaults to: nil)

    URL, file, or '-' for stdin



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/html2rss/cli.rb', line 78

def recon(target = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
  with_recon_targets(target) do |urls, batch_mode|
    results = Html2rss::Recon.batch(
      urls,
      strategy: current_strategy,
      cache_dir: options[:cache_dir]
    )
    filtered = filter_recon_results(results, options[:verdict])
    assert_recon_verdict_match!(filtered, results, batch_mode:)

    Render.recon_output(
      filtered,
      format: options[:format],
      batch_mode:,
      url_only: options[:url_only]
    )

    return if batch_mode || filtered.empty?

    exit(3) if filtered.first.defer?
    exit(1) if filtered.first.drop?
  end
end

#schemavoid

This method returns an undefined value.



236
237
238
239
240
241
242
243
244
245
# File 'lib/html2rss/cli.rb', line 236

def schema
  schema_json = Html2rss.schema_json(pretty: options.fetch(:pretty, true))
  if options[:write]
    FileUtils.mkdir_p(File.dirname(options[:write]))
    File.write(options[:write], "#{schema_json}\n")
    puts options[:write]
  else
    puts schema_json
  end
end

#scrape(url = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • url (String, nil) (defaults to: nil)


218
219
220
221
# File 'lib/html2rss/cli.rb', line 218

def scrape(url = nil)
  strategy, local_file_path, url = prepare_auto_inputs(url, options[:input])
  run_feed_command { scrape_feed_result_for(url, strategy, local_file_path) }
end

#test(config_input = nil, feed_name = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • config_input (String, nil) (defaults to: nil)

    YAML file path, content, or '-' for stdin

  • feed_name (String, nil) (defaults to: nil)

    optional feed name

Raises:

  • (Thor::Error)


159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/html2rss/cli.rb', line 159

def test(config_input = nil, feed_name = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
  raw_content, input_source = read_config_input(config_input)
  result = Html2rss.test(
    raw_content,
    feed_name,
    min_items: options.fetch(:min_items, 1).to_i,
    params: options[:params] || {},
    strategy: options[:strategy],
    strict_quality: options.fetch(:strict_quality, false),
    compare_enhance: options.fetch(:compare_enhance, false)
  )

  if options[:json]
    puts JSON.pretty_generate(result.to_h)
  elsif options[:quiet]
    $stderr.puts(result.error_message) unless result.success # rubocop:disable Style/StderrPuts
  elsif input_source == 'stdin' && !$stdout.tty? && !options[:xml]
    # Piped filter: echo config to stdout on success, write failure to stderr
    if result.success
      puts raw_content
    else
      $stderr.puts "Test failed: #{result.error_message}" # rubocop:disable Style/StderrPuts
    end
  else
    Render.test_card(result, input_source)
    puts result.rss if options[:xml] && result.success
  end

  raise Thor::Error, (result.error_message || 'Test failed') unless result.success
end

#validate(*files) ⇒ void

This method returns an undefined value.

Parameters:

  • files (Array<String>)

    file paths or globs



228
229
230
# File 'lib/html2rss/cli.rb', line 228

def validate(*files)
  Validate.run(files, params: options[:params] || {}, quiet: options[:quiet])
end