Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

About this user

Sam McCall

« Newer Snippets
Older Snippets »
Showing 1-1 of 1 total  RSS 

WoW auction house search library

Library to search WoW auction houses using the www.auctionwowhouse.com site's web service
Example usage;
eldre = Warcraft::AuctionHouse.new("Eldre'Thalas","Alliance")
gems = eldre.query("solid star of elune", :sort => 'bid', :order => 'asc')
puts gems[0] unless gems.empty?

[Solid Star of Elune]: 65g/70g (14:15 left)
require 'net/http'
require 'delegate'

module Warcraft
  # Stores details of an auction
  Auction = Struct.new(:name,:quality,:quantity,:seller,:bid,:buyout,:time) unless Auction
  class Auction
    def to_s
      times = if quantity > 1 then "x#{quantity}" end
      bo = if buyout then "/#{buyout}" end
      "[#{name}]#{times}: #{bid}#{bo} (#{time} left)"
    end
  end
  CategoryInfo = 
<<-EOF.strip
weapon
 1h axe
 2h axe
 bow
 gun
 1h mace
 2h mace
 polearm
 1h sword
 2h sword
 staff
 fist
 misc
 dagger
 thrown
 crossbow
 wand
 fishing pole
armor
 miscellaneous
 cloth
 leather
 mail
 plate
 shield
 libram
 idol
 totem
container
 bag
 soul bag
 herb bag
 enchanting bag
 engineering bag
 gem bag
 mining bag
consumable
trade good
projectile
 arrow
 bullet
quiver
 quiver
 ammo pouch
recipe
 book
 leather
 tailor
 engineering
 blacksmithing
 cooking
 alchemy
 first aid
 enchanting
 jewelcrafting
reagent
misc
EOF
  ItemClasses=Hash.new {|h,k| raise "Unknown class #{k}"}
  ItemSubclasses=Hash.new {|h,k| raise "Unknown subclass #{k}"}
  ItemHierarchy = [ItemClasses,ItemSubclasses]
  ItemClasses[nil]=ItemSubclasses[nil]=nil
  CategoryInfo.each_with_index {|line,i|
    line =~ /^( *)(\w.*?)(\s*)$/
    ItemHierarchy[$1.length][$2]=i+1
  }
  Qualities = {
    "common" => 1,
    "white" => 1,
    "uncommon" => 2,
    "green" => 2,
    "rare" => 3,
    "blue" => 3,
    "epic" => 4,
    "purple" => 4
  }
  Sorting = {
    "name" => 6,
    "level" => 2,
    "time" => 3,
    "seller" => 7,
    "bid" => 4,
    "price" => 4
  }
  Order = {
    "ascending" => 0,
    "asc" => 0,
    "desc" => 1,
    "descending" => 1
  }
  
  # Represents an AH to query. 
  class AuctionHouse
    # e.g. AuctionHouse.new("Eldre'Thalas","Alliance")
    # Neutral AH are not available
    # Third parameter is 'EU' if you're on a european server.
    def initialize(realm,faction,locale="US")
      @realm,@locale = realm,locale
      @faction = case faction.to_s.downcase
      when "alliance"
        "Alliance"
      when "horde"
        "Horde"
      end
    end
    
    # Main entry point.
    # Usage: query("item name", :opt1 => "value", ...)
    # Item name can be nil. 
    # Options are:
    #  :min     minimum level item
    #  :max     maximum level item
    #  :quality white/green/blue/purple minimum item quality
    #  :type    weapon/armor/consumable/trade good/recipe etc
    #  :subtype 1h axe/enchanting/soul bag etc - must have the correct type specified too
    #  :seller  seller to search for
    #  :sort    name/level/time/bid/seller sorting method
    #  :order   asc/desc sort order
    #  :page    which page of results (1-based). 10 returned at a time.
    # Returns an array of Warcraft::Auctions.
    def query(name, opts={})
      params = {}
      params["realm"] = "#@realm #@locale" #"Eldre'Thalas US"
      params["faction"] = if @faction=="Alliance" then 1 else 2 end
      params["ItemName"] = name
      params["LevelStart"] = opts[:min] # Level range to filter by
      params["LevelEnd"] = opts[:max]
      params["Seller"] = opts[:seller]
      params["Rarity"] = Qualities[opts[:quality]]
      params["itemClassID"] = ItemClasses[opts[:type]] 
      params["itemSubClass"] = ItemSubclasses[opts[:subtype]] 
      params["pagenum"] = (opts[:page] || 1).to_s
      params["invenTypeID"] = nil # think this is the slot for armor searches - e.g. armor -> leather -> *shoulder*. unimplemented.
      params["sort_column"] = Sorting[opts[:sort]]
      params["sort_order"] = Order[opts[:order]]
      
      querytext = MiniJSON::encode(params)
      
      puts "Query:\n#{querytext}" if $DEBUG
      
      retrieve_results(querytext)
    end
    
    def retrieve_results(querytext)
      # We just post the JSON to the given URL and get a JSON encoded string back
      req = Net::HTTP::Post.new("/AuctionInfo.asmx/getAuctionInfo")
      req["Content-Type"]="application/json"
      response = Net::HTTP.new("www.auctionwowhouse.com",80).start {|http| http.request(req,querytext)}
      response.error! unless Net::HTTPSuccess === response
      resulttext = response.body
      resulttext = MiniJSON::decode(resulttext)
      puts "Response:\n#{resulttext}" if $DEBUG
      
      parse_results(resulttext)
    end
    
    # Web service returns a blob of ugly html, so we scrape the data out of it. Cover your eyes...
    def parse_results(resulttext)
      items = []

      resulttext.scan(/<TR style='height.*?>(.*?)<\/TR>/) {|text,| # A row of the main table containing an item
        puts "Item:\n#{text}" if $DEBUG
        item = Auction.new
        text.scan(/<font color=(.*?)>(.*?)<\/font>/) {|color,name|
          item.quality = color
          item.name = name
        }
        text.scan(/<td width="81" .*?>(.*?)<\/td>/) {|name,|
          item.seller = name
        }
        text.scan(/<td width="75" .*?>(.*?)<\/td>/) {|q,|
          item.quantity = q.to_i
        }
    		text.scan(/<td width="82" .*?><div .*?>(.*?)<\/div><\/td>/) {|time,|
    		  item.time = Duration.new(time.to_i)
  		  }
  		  text.scan(%r{(?:(\d+)<img border=0 src="/images/wow_40_jin\.gif">)?(?:(\d+)<img border=0 src="/images/wow_42_yin\.gif">)?(\d+)<img border=0 src="/images/wow_44_tong\.gif"><BR>(?:(?:(\d+)<img border=0 src="/images/wow_40_jin\.gif">)?(?:(\d+)<img border=0 src="/images/wow_42_yin\.gif">)?(\d+)<img border=0 src="/images/wow_44_tong\.gif">)?</a>}) {|bg,bs,bc,g,s,c|
          item.bid = Price.new(10000*bg.to_i + 100 * bs.to_i + bc.to_i)
          item.buyout = Price.new(10000*g.to_i + 100 * s.to_i + c.to_i) if g #buyout may be absent
        }
        items << item
      }
      
      items
    end
  end
  
  # Chunk of money (gold/silver/copper)
  class Price < DelegateClass(Integer); end unless Price # So we can reload the library without throwing
  class Price
    def gold; self / 10000; end
    def silver; (self / 100)%100; end
    def copper; self % 100; end
    def to_a; [gold,silver,copper]; end
    def inspect; "<#{self.class} #{self.to_i}>"; end
    
    # to display we pick the dominant unit (gold if it's at least 1g, else silver if it's at 
    # least 1s, else copper) and round to the nearest
    def to_s
      big,little,letter = 
        if gold > 0
          [gold,silver,"g"]
        elsif silver > 0
          [silver,copper,"s"]
        else
          [copper,0,"c"]
        end
      big += 1 if little >= 50
      "#{big}#{letter}" 
    end
    
  end
  
  # Measure of time (hours-minutes-seconds)
  class Duration < DelegateClass(Integer); end unless Duration
  class Duration
    def hours; self / 3600; end
    def minutes; (self / 60)%60; end
    def seconds; self % 60; end
    def to_a; [hours,minutes,seconds] end
    def to_s; "%d:%02d" % self.to_a; end # just show hours and minutes
    def inspect; "<#{self.class} #{to_s}>"; end
  end
  
  #Bare minimum JSON support to talk to the service
  module MiniJSON
    # Encode an object - we only support strings, numbers, and hashes
    def self.encode(x)
      case x
      when String
        "\"#{x.gsub('"','\\"')}\""
      when Numeric
        x.to_s
      when Hash
        "{" + x.map{|k,v| encode(k)+": "+encode(v)}.join(", ") + "}"
      when NilClass
        "\"\""
      else
        raise "Can't encode #{x.class}"
      end
    end
    # Decode an object - only supports a single string literal
    def self.decode(x)
      x.strip!
      if x.length > 1 and x[0]==?" and x[-1]==?" and x[-2]!=?\\
        x[1..-2].gsub('\\"','"').gsub("\\'","'").gsub(/\\u([a-fA-F0-9]{4})/) {[$1.hex].pack("U") }
      else
        raise "Can't decode #{x.inspect}"
      end
    end
  end
end
« Newer Snippets
Older Snippets »
Showing 1-1 of 1 total  RSS