# frozen_string_literal: true require "json" require "net/http" require "provider/adapters/adapter" class Provider module Adapters # Bank of Jamaica daily counter rates in JMD. # Fetches from a WPDataTables endpoint (table_id=134), which requires a nonce extracted from the page HTML. class BOJA < Adapter PAGE_URL = "https://boj.org.jm/market/foreign-exchange/counter-rates/" BASE_URL = "https://boj.org.jm/wp-admin/admin-ajax.php" TABLE_ID = 134 NONCE_PATTERN = /wdtNonceFrontendServerSide_#{TABLE_ID}"\s+value="([^"]+)"/ CURRENCY_MAP = { "U.S. DOLLAR" => "USD", "GREAT BRITAIN POUND" => "GBP", "CANADA DOLLAR" => "CAD", "EURO" => "EUR", "JAPANESE YEN" => "JPY", "SWISS FRANC" => "CHF", "AUSTRALIAN DOLLAR" => "AUD", "DANISH KRONE" => "DKK", "NORWEGIAN KRONE" => "NOK", "SWEDISH KRONA" => "SEK", "HONG KONG DOLLAR" => "HKD", "BARBADOS DOLLAR" => "BBD", "BELIZE DOLLAR" => "BZD", "T&T DOLLAR" => "TTD", "BAHAMAS DOLLAR" => "BSD", "CAYMAN DOLLAR" => "KYD", "GUYANA DOLLAR" => "GYD", "E. C. DOLLAR" => "XCD", "DOMINICAN REP. PESO" => "DOP", "GIBRALTAR POUND" => "GIP", "NORTHERN IRELAND POUND" => "GBP", }.freeze def fetch(after: nil, upto: nil) dataset = fetch_table dataset = dataset.select { |r| r[:date] >= after } if after dataset = dataset.select { |r| r[:date] <= upto } if upto dataset end def parse(json) data = json.is_a?(String) ? JSON.parse(json) : json rows = data["data"] return [] unless rows.is_a?(Array) rows.filter_map do |row| parse_row(row) end end private def fetch_table nonce = fetch_nonce return [] unless nonce uri = URI("#{BASE_URL}?action=get_wdtable&table_id=#{TABLE_ID}") request = Net::HTTP::Post.new(uri) request.set_form_data("draw" => "1", "start" => "0", "length" => "-1", "wdtNonce" => nonce) response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end parse(response.body) end def fetch_nonce uri = URI(PAGE_URL) response = Net::HTTP.get(uri) match = response.match(NONCE_PATTERN) match&.captures&.first end def parse_row(row) date_str, currency_name, sell_rate, buy_rate, = row return unless date_str && currency_name date = Date.strptime(date_str.strip, "%d %b %Y") iso = resolve_currency(currency_name.strip) return unless iso rate_value = compute_mid(sell_rate, buy_rate) return unless rate_value && !rate_value.zero? { date:, base: iso, quote: "JMD", rate: rate_value } end def compute_mid(sell, buy) s = Float(sell) b = Float(buy) return s if b.zero? (s + b) / 2.0 end def resolve_currency(name) CURRENCY_MAP[name.upcase] end end end end