Add scraping for Styling Studio "Restyle" exclusives
This commit is contained in:
parent
75769304c1
commit
c51dd611a5
3 changed files with 127 additions and 4 deletions
|
|
@ -120,6 +120,39 @@ module Neopets::NCMall
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Load "exclusive" styles from tab 3 of the Styling Studio. Unlike tabs 1
|
||||||
|
# and 2, these are not species-specific—there's just one request that
|
||||||
|
# returns all exclusives.
|
||||||
|
def self.load_exclusives(neologin:)
|
||||||
|
Sync do
|
||||||
|
DTIRequests.post(
|
||||||
|
STYLING_STUDIO_URL,
|
||||||
|
[
|
||||||
|
["Content-Type", "application/x-www-form-urlencoded"],
|
||||||
|
["Cookie", "neologin=#{neologin}"],
|
||||||
|
["X-Requested-With", "XMLHttpRequest"],
|
||||||
|
],
|
||||||
|
{tab: 3, mode: "getTab", key: "StylingStudioTab", value: 3}.to_query,
|
||||||
|
) do |response|
|
||||||
|
if response.status != 200
|
||||||
|
raise ResponseNotOK.new(response.status),
|
||||||
|
"expected status 200 but got #{response.status} (#{STYLING_STUDIO_URL})"
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
data = JSON.parse(response.read).deep_symbolize_keys
|
||||||
|
|
||||||
|
# Like styles, exclusives is a hash keyed by ID (or an empty
|
||||||
|
# array when there are none).
|
||||||
|
data.fetch(:exclusives).to_h.values.
|
||||||
|
map { |s| s.slice(:oii, :name, :image) }
|
||||||
|
rescue JSON::ParserError, KeyError
|
||||||
|
raise UnexpectedResponseFormat
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Generate a new image hash for a pet wearing specific items. Takes a base
|
# 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
|
# pet sci (species/color image hash) and optional item IDs, and returns a
|
||||||
# response containing the combined image hash in the :newsci field.
|
# response containing the combined image hash in the :newsci field.
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,34 @@ namespace "neopets:import" do
|
||||||
end
|
end
|
||||||
print "\n"
|
print "\n"
|
||||||
|
|
||||||
style_ids = styles_by_species_id.values.flatten(1).map { |s| s[:oii] }
|
# Load exclusive styles from tab 3 (not species-specific).
|
||||||
|
print "Loading exclusives…"
|
||||||
|
begin
|
||||||
|
exclusives = Neopets::NCMall.load_exclusives(
|
||||||
|
neologin: Neologin.cookie,
|
||||||
|
)
|
||||||
|
rescue => error
|
||||||
|
puts "\n⚠️ Error loading exclusives, skipping: #{error.message}"
|
||||||
|
Sentry.capture_exception(error,
|
||||||
|
tags: { task: "neopets:import:styling_studio" })
|
||||||
|
exclusives = nil
|
||||||
|
end
|
||||||
|
puts " #{exclusives&.size || 0} loaded"
|
||||||
|
|
||||||
|
all_styles = styles_by_species_id.values.flatten(1)
|
||||||
|
all_styles += exclusives unless exclusives.nil?
|
||||||
|
style_ids = all_styles.map { |s| s[:oii] }
|
||||||
style_records_by_id =
|
style_records_by_id =
|
||||||
AltStyle.where(id: style_ids).to_h { |as| [as.id, as] }
|
AltStyle.where(id: style_ids).to_h { |as| [as.id, as] }
|
||||||
|
|
||||||
all_species.each do |species|
|
# Build a list of groups to process: one per species, plus exclusives.
|
||||||
styles = styles_by_species_id[species.id]
|
groups = all_species.map { |sp|
|
||||||
|
{label: sp.human_name, styles: styles_by_species_id[sp.id]}
|
||||||
|
}
|
||||||
|
groups << {label: "Exclusives", styles: exclusives}
|
||||||
|
|
||||||
|
groups.each do |group|
|
||||||
|
styles = group[:styles]
|
||||||
next if styles.nil?
|
next if styles.nil?
|
||||||
|
|
||||||
counts = {changed: 0, unchanged: 0, skipped: 0}
|
counts = {changed: 0, unchanged: 0, skipped: 0}
|
||||||
|
|
@ -96,7 +118,7 @@ namespace "neopets:import" do
|
||||||
record.save!
|
record.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "#{species.human_name}: #{counts[:changed]} changed, " +
|
puts "#{group[:label]}: #{counts[:changed]} changed, " +
|
||||||
"#{counts[:unchanged]} unchanged, #{counts[:skipped]} skipped"
|
"#{counts[:unchanged]} unchanged, #{counts[:skipped]} skipped"
|
||||||
end
|
end
|
||||||
rescue => e
|
rescue => e
|
||||||
|
|
|
||||||
|
|
@ -213,4 +213,72 @@ RSpec.describe Neopets::NCMall, type: :model do
|
||||||
expect { styles }.to raise_error(Neopets::NCMall::UnexpectedResponseFormat)
|
expect { styles }.to raise_error(Neopets::NCMall::UnexpectedResponseFormat)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe ".load_exclusives" do
|
||||||
|
def stub_exclusives_request
|
||||||
|
stub_request(:post, "https://www.neopets.com/np-templates/ajax/stylingstudio/studio.php").
|
||||||
|
with(
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
"Cookie": "neologin=STUB_NEOLOGIN",
|
||||||
|
"User-Agent": Rails.configuration.user_agent_for_neopets,
|
||||||
|
},
|
||||||
|
body: "key=StylingStudioTab&mode=getTab&tab=3&value=3",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject(:exclusives) do
|
||||||
|
Neopets::NCMall.load_exclusives(neologin: "STUB_NEOLOGIN")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "loads exclusive styles from tab 3" do
|
||||||
|
stub_exclusives_request.to_return(
|
||||||
|
body: '{"success":true,"exclusives":{"95639":{"name":"Treasured Roberta","image":"https:\/\/images.neopets.com\/items\/1c2h6d7fdn.gif","oii":95639},"95640":{"name":"Treasured Lisha","image":"https:\/\/images.neopets.com\/items\/0ocf7m2daj.gif","oii":95640}},"styleMeter":{"num":0,"max":15,"isReady":false},"mode":"getTab"}'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(exclusives).to contain_exactly(
|
||||||
|
{
|
||||||
|
oii: 95639,
|
||||||
|
name: "Treasured Roberta",
|
||||||
|
image: "https://images.neopets.com/items/1c2h6d7fdn.gif",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oii: 95640,
|
||||||
|
name: "Treasured Lisha",
|
||||||
|
image: "https://images.neopets.com/items/0ocf7m2daj.gif",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles empty exclusives" do
|
||||||
|
stub_exclusives_request.to_return(
|
||||||
|
body: '{"success":true,"exclusives":[],"styleMeter":{"num":0,"max":15,"isReady":false},"mode":"getTab"}'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(exclusives).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error if the request returns a non-200 status" do
|
||||||
|
stub_exclusives_request.to_return(status: 400)
|
||||||
|
|
||||||
|
expect { exclusives }.to raise_error(Neopets::NCMall::ResponseNotOK)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error if the request returns a non-JSON response" do
|
||||||
|
stub_exclusives_request.to_return(
|
||||||
|
body: "Oops, this request failed for some weird reason!",
|
||||||
|
)
|
||||||
|
|
||||||
|
expect { exclusives }.to raise_error(Neopets::NCMall::UnexpectedResponseFormat)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error if the request returns unexpected JSON" do
|
||||||
|
stub_exclusives_request.to_return(
|
||||||
|
body: '{"success": false}',
|
||||||
|
)
|
||||||
|
|
||||||
|
expect { exclusives }.to raise_error(Neopets::NCMall::UnexpectedResponseFormat)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue