From 7430e12655c99c15b45a7b28d12f01aca522ca30 Mon Sep 17 00:00:00 2001 From: Emi Matchu Date: Sun, 4 Jan 2026 18:38:01 -0800 Subject: [PATCH] Extend `rails pets:load` to look up arbitrary pets & items --- app/services/neopets/nc_mall.rb | 38 +++++++++++++++++++++++++++++++++ lib/tasks/pets.rake | 32 ++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/app/services/neopets/nc_mall.rb b/app/services/neopets/nc_mall.rb index bc740fe0..d160a2d7 100644 --- a/app/services/neopets/nc_mall.rb +++ b/app/services/neopets/nc_mall.rb @@ -120,6 +120,44 @@ module Neopets::NCMall end end + # Generate a new image hash for a pet wearing specific items. Takes a base + # pet sci (species/color image hash) and optional item IDs, and returns a + # response containing the combined image hash in the :newsci field. + # Use the returned hash with Neopets::CustomPets.fetch_viewer_data("@#{newsci}") + # to get the full appearance data. + PET_DATA_URL = "https://ncmall.neopets.com/mall/ajax/petview/getPetData.php" + def self.fetch_pet_data(pet_sci, item_ids = []) + Sync do + params = {"selPetsci" => pet_sci} + item_ids.each { |id| params["itemsList[]"] = id.to_s } + + DTIRequests.post( + PET_DATA_URL, + [["Content-Type", "application/x-www-form-urlencoded"]], + params.to_query, + ) do |response| + if response.status != 200 + raise ResponseNotOK.new(response.status), + "expected status 200 but got #{response.status} (#{PET_DATA_URL})" + end + + begin + data = JSON.parse(response.read) + rescue JSON::ParserError + raise UnexpectedResponseFormat, + "failed to parse pet data response as JSON" + end + + unless data["newsci"].is_a?(String) && data["newsci"].present? + raise UnexpectedResponseFormat, + "missing or invalid field newsci in pet data response" + end + + {newsci: data["newsci"]} + end + end + end + private # Map load_type from menu JSON to the v2 API type parameter. diff --git a/lib/tasks/pets.rake b/lib/tasks/pets.rake index f3c51731..c7c560b7 100644 --- a/lib/tasks/pets.rake +++ b/lib/tasks/pets.rake @@ -1,7 +1,33 @@ namespace :pets do - desc "Load a pet's viewer data" - task :load, [:name] => [:environment] do |task, args| - viewer_data = Neopets::CustomPets.fetch_viewer_data(args[:name]) + desc "Load a pet's viewer data (by name or by color/species/items)" + task :load, [:first] => [:environment] do |task, args| + # Collect all arguments (first + extras) + all_args = [args[:first]] + args.extras + + # If only one argument, treat it as a pet name + if all_args.length == 1 + viewer_data = Neopets::CustomPets.fetch_viewer_data(all_args[0]) + else + # Multiple arguments: color, species, and optional item IDs + color_name = all_args[0] + species_name = all_args[1] + item_ids = all_args[2..] + + # Look up the PetType to get its image hash + pet_type = PetType.matching_name(color_name, species_name).first! + pet_sci = pet_type.image_hash + + # Fetch the new image hash for this pet+items combination + response = Neopets::NCMall.fetch_pet_data(pet_sci, item_ids) + new_sci = response[:newsci] + + # Output the hash to stderr for debugging + $stderr.puts "Generated image hash: #{new_sci}" + + # Load the full viewer data using the new image hash + viewer_data = Neopets::CustomPets.fetch_viewer_data("@#{new_sci}") + end + puts JSON.pretty_generate(viewer_data) end