impress/app/services/neopets/custom_pets.rb
Emi Matchu c48b2b14aa Add workarounds for new Neopets.com security rules
Neopets.com recently added some new security rules that, if not
satisfied, cause the request to return 403 Forbidden.

We figured these out through trial and error, and added them to the
`DTIRequests` library, so they would apply to all requests we make.

We also updated our AMFPHP library to use `DTIRequests` as well, as an
easy way to get the same security rules to apply to those requests.

This change was motivated by pet loading being down for the past day or
so, because all pet loading requests were returning 403 Forbidden! Now,
we've fixed it, hooray!
2025-03-29 14:14:46 -07:00

65 lines
2.2 KiB
Ruby

require 'rocketamf_extensions/remote_gateway'
module Neopets::CustomPets
GATEWAY_URL =
Addressable::URI.parse(Rails.configuration.neopets_origin) +
'/amfphp/gateway.php'
GATEWAY = RocketAMFExtensions::RemoteGateway.new(GATEWAY_URL)
CUSTOM_PET_SERVICE = GATEWAY.service('CustomPetService')
PET_SERVICE = GATEWAY.service('PetService')
class << self
# NOTE: Ideally pet requests shouldn't take this long, but Neopets can be
# slow sometimes! Since we're on the Falcon server, long timeouts shouldn't
# slow down the rest of the request queue, like it used to be in the past.
def fetch_viewer_data(name, timeout: 10)
request = CUSTOM_PET_SERVICE.action('getViewerData').request([name])
send_amfphp_request(request).tap do |data|
if data[:custom_pet][:name].blank?
raise PetNotFound, "Pet #{name.inspect} does not exist"
end
end
end
def fetch_metadata(name, timeout: 10)
# If this is an image hash "pet name", it has no metadata.
return nil if name.start_with?("@")
request = PET_SERVICE.action('getPet').request([name])
send_amfphp_request(request).tap do |data|
if data[:name].blank?
raise PetNotFound, "Pet #{name.inspect} does not exist"
end
end
end
# Given a pet's name, load its image hash, for use in `pets.neopets.com`
# image URLs. (This corresponds to its current biology and items.)
def fetch_image_hash(name, timeout: 10)
# If this is an image hash "pet name", just take off the `@`!
return name[1..] if name.start_with?("@")
metadata = fetch_metadata(name, timeout:)
metadata[:hash]
end
private
# Send an AMFPHP request, re-raising errors as `DownloadError`.
# Return the response body as a `HashWithIndifferentAccess`.
def send_amfphp_request(request, timeout: 10)
begin
response_data = request.post(timeout: timeout)
rescue RocketAMFExtensions::RemoteGateway::AMFError => e
raise DownloadError, e.message
rescue RocketAMFExtensions::RemoteGateway::ConnectionError => e
raise DownloadError, e.message, e.backtrace
end
HashWithIndifferentAccess.new(response_data)
end
end
class PetNotFound < RuntimeError;end
class DownloadError < RuntimeError;end
end