108 lines
2.8 KiB
Ruby
108 lines
2.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'net/http'
|
|
require 'nokogiri'
|
|
|
|
module FicTracker::Backends::Spacebattles
|
|
class Client
|
|
BASE_URL = 'https://forums.spacebattles.com'
|
|
|
|
def initialize
|
|
@pagination_query = nil
|
|
@pagination_path = nil
|
|
end
|
|
|
|
def url
|
|
URI(BASE_URL)
|
|
end
|
|
|
|
def request(path, query: nil, method: :get)
|
|
path = File.join(path, @pagination_path) if @pagination_path
|
|
uri = URI.join(url, path)
|
|
query = @pagination_query.merge query if @pagination_query
|
|
uri.query = URI.encode_www_form(query) if query
|
|
|
|
req = Net::HTTP.const_get(method.to_s.capitalize.to_sym).new uri.request_uri
|
|
req['User-Agent'] = "FicTracker/#{FicTracker::VERSION}"
|
|
req['Accept'] = 'text/html'
|
|
|
|
resp = nil
|
|
http.start do
|
|
loop do
|
|
debug_http(req)
|
|
resp = http.request req
|
|
debug_http(resp)
|
|
case resp
|
|
when Net::HTTPRedirection
|
|
uri = URI.join(url, resp['location'])
|
|
uri.query = URI.encode_www_form(query) if query
|
|
req.path.replace uri.request_uri
|
|
when Net::HTTPTooManyRequests
|
|
wait_time = 10
|
|
if resp['retry-after']
|
|
after = resp['retry-after']
|
|
wait_time = after =~ /\A[0-9]+\Z/ ? after.to_i : Time.parse(after)
|
|
end
|
|
wait_time = Time.now + wait_time if wait_time.is_a? Numeric
|
|
|
|
logger.info "Rate limited, waiting until #{wait_time} before retrying"
|
|
sleep wait_time - Time.now
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
resp.value
|
|
return if resp.body.empty?
|
|
|
|
Nokogiri::HTML4.parse(resp.body)
|
|
end
|
|
|
|
def paginated(style, per_page: 200, &_block)
|
|
raise 'Style must be :path or :query' unless %i[path query].include? style
|
|
@pagination_query = { per_page: per_page } if style == :query
|
|
@pagination_path = '' if style == :path
|
|
page = 1
|
|
|
|
loop do
|
|
result = yield
|
|
|
|
return result unless result.at_css('nav.pageNavWrapper .pageNav-jump--next')
|
|
|
|
page += 1
|
|
@pagination_query[:page] = page if style == :query
|
|
@pagination_path = "page-#{page}" if style == :path
|
|
end
|
|
ensure
|
|
@pagination_query = nil
|
|
@pagination_path = nil
|
|
end
|
|
|
|
private
|
|
|
|
def logger
|
|
Logging.logger[self]
|
|
end
|
|
|
|
def http
|
|
@http ||= Net::HTTP.new(url.host, url.port).tap { |http| http.use_ssl = url.scheme == 'https' }
|
|
end
|
|
|
|
def debug_http(object)
|
|
return unless logger.debug?
|
|
|
|
dir = '>'
|
|
if object.is_a?(Net::HTTPRequest)
|
|
dir = '<'
|
|
|
|
logger.debug "#{dir} #{object.method} #{object.path}"
|
|
else
|
|
logger.debug "#{dir} #{object.code} #{object.message}"
|
|
end
|
|
|
|
object.each_header { |h, v| logger.debug "#{dir} #{h}: #{v}" }
|
|
end
|
|
end
|
|
end
|
|
|