Compare commits
No commits in common. "abfe1e6df7dcc3b84752dff2cec8a79eb81bf43a" and "23c083ff1d0056b3c56e42795823db468039c615" have entirely different histories.
abfe1e6df7
...
23c083ff1d
10 changed files with 79 additions and 85 deletions
|
@ -161,7 +161,7 @@ class AuthUser < AuthRecord
|
|||
# means we can wrap it in a `with_timeout` block!)
|
||||
neopets_username = Sync do |task|
|
||||
task.with_timeout(5) do
|
||||
Neopets::NeoPass.load_main_neopets_username(auth.credentials.token)
|
||||
NeoPass.load_main_neopets_username(auth.credentials.token)
|
||||
end
|
||||
rescue Async::TimeoutError
|
||||
nil # If the request times out, just move on!
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
require 'rocketamf_extensions/remote_gateway'
|
||||
require 'ostruct'
|
||||
|
||||
class Pet < ApplicationRecord
|
||||
NEOPETS_URL_ORIGIN = ENV['NEOPETS_URL_ORIGIN'] || 'https://www.neopets.com'
|
||||
GATEWAY_URL = NEOPETS_URL_ORIGIN + '/amfphp/gateway.php'
|
||||
GATEWAY = RocketAMFExtensions::RemoteGateway.new(GATEWAY_URL)
|
||||
CUSTOM_PET_SERVICE = GATEWAY.service('CustomPetService')
|
||||
PET_SERVICE = GATEWAY.service('PetService')
|
||||
|
||||
belongs_to :pet_type
|
||||
|
||||
attr_reader :items, :pet_state, :alt_style
|
||||
|
@ -8,7 +17,7 @@ class Pet < ApplicationRecord
|
|||
}
|
||||
|
||||
def load!(timeout: nil)
|
||||
viewer_data = Neopets::CustomPets.fetch_viewer_data(name, timeout:)
|
||||
viewer_data = self.class.fetch_viewer_data(name, timeout:)
|
||||
use_viewer_data(viewer_data)
|
||||
end
|
||||
|
||||
|
@ -27,7 +36,7 @@ class Pet < ApplicationRecord
|
|||
)
|
||||
|
||||
begin
|
||||
new_image_hash = Neopets::CustomPets.fetch_image_hash(self.name)
|
||||
new_image_hash = Pet.fetch_image_hash(self.name)
|
||||
rescue => error
|
||||
Rails.logger.warn "Failed to load image hash: #{error.full_message}"
|
||||
end
|
||||
|
@ -114,5 +123,61 @@ class Pet < ApplicationRecord
|
|||
pet.load!(**options)
|
||||
pet
|
||||
end
|
||||
|
||||
# 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 self.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 self.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 self.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
|
||||
|
||||
class PetNotFound < RuntimeError;end
|
||||
class DownloadError < RuntimeError;end
|
||||
class UnexpectedDataFormat < RuntimeError;end
|
||||
|
||||
private
|
||||
|
||||
# Send an AMFPHP request, re-raising errors as `Pet::DownloadError`.
|
||||
# Return the response body as a `HashWithIndifferentAccess`.
|
||||
def self.send_amfphp_request(request, timeout: 10)
|
||||
begin
|
||||
response_data = request.post(timeout: timeout, headers: {
|
||||
"User-Agent" => Rails.configuration.user_agent_for_neopets,
|
||||
})
|
||||
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
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require "addressable/template"
|
||||
require "async/http/internet/instance"
|
||||
|
||||
module Neopets::NCMall
|
||||
module NCMall
|
||||
# Share a pool of persistent connections, rather than reconnecting on
|
||||
# each request. (This library does that automatically!)
|
||||
INTERNET = Async::HTTP::Internet.instance
|
|
@ -2,7 +2,7 @@ require "async/http/internet/instance"
|
|||
|
||||
# While most of our NeoPass logic is built into Devise -> OmniAuth -> OIDC
|
||||
# OmniAuth plugin, NeoPass also offers some supplemental APIs that we use here.
|
||||
module Neopets::NeoPass
|
||||
module NeoPass
|
||||
# Share a pool of persistent connections, rather than reconnecting on
|
||||
# each request. (This library does that automatically!)
|
||||
INTERNET = Async::HTTP::Internet.instance
|
|
@ -1,64 +0,0 @@
|
|||
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 `Pet::DownloadError`.
|
||||
# Return the response body as a `HashWithIndifferentAccess`.
|
||||
def send_amfphp_request(request, timeout: 10)
|
||||
begin
|
||||
response_data = request.post(timeout: timeout, headers: {
|
||||
"User-Agent" => Rails.configuration.user_agent_for_neopets,
|
||||
})
|
||||
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
|
||||
end
|
|
@ -7,9 +7,8 @@
|
|||
|
||||
:markdown
|
||||
Pet Styles drastically change the appearance of your pet! They're [available
|
||||
in the NC Mall][1], or via "NC Trading". Some of them are "Nostalgic",
|
||||
meaning they're reminiscent of classic Neopets designs from long ago—and some
|
||||
are brand new!
|
||||
in the NC Mall][1], or via "NC Trading". They're generally reminiscent of
|
||||
classic Neopets designs from long ago.
|
||||
|
||||
Pet Styles only fit pets of the same species—but the *color* of the pet
|
||||
doesn't matter! A Blue Acara can wear the "Nostalgic Faerie Acara" Pet Style.
|
||||
|
|
|
@ -72,12 +72,6 @@ module OpenneoImpressItems
|
|||
# version number, etc. So let's only send this to Neopets systems, where it
|
||||
# should hopefully be clear who we are from context!
|
||||
config.user_agent_for_neopets = "Dress to Impress"
|
||||
|
||||
# Use the usual Neopets.com, unless we have an override. (At times, we've
|
||||
# used this in collaboration with TNT to address the server directly,
|
||||
# instead of through the CDN.)
|
||||
config.neopets_origin =
|
||||
ENV.fetch('NEOPETS_URL_ORIGIN', 'https://www.neopets.com')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,10 +19,10 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|||
# Teach Zeitwerk that `RocketAMF` is what to expect in `lib/rocketamf`.
|
||||
inflect.acronym "RocketAMF"
|
||||
|
||||
# Teach Zeitwerk that `NeoPass` is what to expect in `neopass.rb`.
|
||||
# Teach Zeitwerk that `NeoPass` is what to expect in `app/services/neopass.rb`.
|
||||
inflect.acronym "NeoPass"
|
||||
|
||||
# Teach Zeitwerk that "NCMall" is what to expect in `nc_mall.rb`.
|
||||
# Teach Zeitwerk that "NCMall" is what to expect in `app/services/nc_mall.rb`.
|
||||
# (We do this by teaching it the word "NC".)
|
||||
inflect.acronym "NC"
|
||||
end
|
||||
|
|
|
@ -77,17 +77,17 @@ end
|
|||
def load_all_nc_mall_pages
|
||||
Sync do
|
||||
# First, start loading the homepage.
|
||||
homepage_task = Async { Neopets::NCMall.load_home_page }
|
||||
homepage_task = Async { NCMall.load_home_page }
|
||||
|
||||
# Next, load the page links for different categories etc.
|
||||
links = Neopets::NCMall.load_page_links
|
||||
links = NCMall.load_page_links
|
||||
|
||||
# Next, load the linked pages, 10 at a time.
|
||||
barrier = Async::Barrier.new
|
||||
semaphore = Async::Semaphore.new(10, parent: barrier)
|
||||
begin
|
||||
linked_page_tasks = links.map do |link|
|
||||
semaphore.async { Neopets::NCMall.load_page link[:type], link[:cat] }
|
||||
semaphore.async { NCMall.load_page link[:type], link[:cat] }
|
||||
end
|
||||
barrier.wait # Load all the pages.
|
||||
ensure
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace :pets do
|
||||
desc "Load a pet's viewer data"
|
||||
task :load, [:name] => [:environment] do |task, args|
|
||||
pp Neopets::CustomPets.fetch_viewer_data(args[:name])
|
||||
pp Pet.fetch_viewer_data(args[:name])
|
||||
end
|
||||
|
||||
desc "Find pets that were, last we saw, of the given color and species"
|
||||
|
|
Loading…
Reference in a new issue