<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: auctionhouse code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Wed, 20 Aug 2008 03:48:19 GMT</pubDate>
    <description>DZone Snippets: auctionhouse code</description>
    <item>
      <title>WoW auction house search library</title>
      <link>http://snippets.dzone.com/posts/show/3811</link>
      <description>Library to search WoW auction houses using the www.auctionwowhouse.com site's web service&lt;br /&gt;Example usage;&lt;br /&gt;eldre = Warcraft::AuctionHouse.new("Eldre'Thalas","Alliance")&lt;br /&gt;gems = eldre.query("solid star of elune", :sort =&gt; 'bid', :order =&gt; 'asc')&lt;br /&gt;puts gems[0] unless gems.empty?&lt;br /&gt;&lt;br /&gt;[Solid Star of Elune]: 65g/70g (14:15 left)&lt;br /&gt;&lt;code&gt;&lt;br /&gt;require 'net/http'&lt;br /&gt;require 'delegate'&lt;br /&gt;&lt;br /&gt;module Warcraft&lt;br /&gt;  # Stores details of an auction&lt;br /&gt;  Auction = Struct.new(:name,:quality,:quantity,:seller,:bid,:buyout,:time) unless Auction&lt;br /&gt;  class Auction&lt;br /&gt;    def to_s&lt;br /&gt;      times = if quantity &gt; 1 then "x#{quantity}" end&lt;br /&gt;      bo = if buyout then "/#{buyout}" end&lt;br /&gt;      "[#{name}]#{times}: #{bid}#{bo} (#{time} left)"&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  CategoryInfo = &lt;br /&gt;&lt;&lt;-EOF.strip&lt;br /&gt;weapon&lt;br /&gt; 1h axe&lt;br /&gt; 2h axe&lt;br /&gt; bow&lt;br /&gt; gun&lt;br /&gt; 1h mace&lt;br /&gt; 2h mace&lt;br /&gt; polearm&lt;br /&gt; 1h sword&lt;br /&gt; 2h sword&lt;br /&gt; staff&lt;br /&gt; fist&lt;br /&gt; misc&lt;br /&gt; dagger&lt;br /&gt; thrown&lt;br /&gt; crossbow&lt;br /&gt; wand&lt;br /&gt; fishing pole&lt;br /&gt;armor&lt;br /&gt; miscellaneous&lt;br /&gt; cloth&lt;br /&gt; leather&lt;br /&gt; mail&lt;br /&gt; plate&lt;br /&gt; shield&lt;br /&gt; libram&lt;br /&gt; idol&lt;br /&gt; totem&lt;br /&gt;container&lt;br /&gt; bag&lt;br /&gt; soul bag&lt;br /&gt; herb bag&lt;br /&gt; enchanting bag&lt;br /&gt; engineering bag&lt;br /&gt; gem bag&lt;br /&gt; mining bag&lt;br /&gt;consumable&lt;br /&gt;trade good&lt;br /&gt;projectile&lt;br /&gt; arrow&lt;br /&gt; bullet&lt;br /&gt;quiver&lt;br /&gt; quiver&lt;br /&gt; ammo pouch&lt;br /&gt;recipe&lt;br /&gt; book&lt;br /&gt; leather&lt;br /&gt; tailor&lt;br /&gt; engineering&lt;br /&gt; blacksmithing&lt;br /&gt; cooking&lt;br /&gt; alchemy&lt;br /&gt; first aid&lt;br /&gt; enchanting&lt;br /&gt; jewelcrafting&lt;br /&gt;reagent&lt;br /&gt;misc&lt;br /&gt;EOF&lt;br /&gt;  ItemClasses=Hash.new {|h,k| raise "Unknown class #{k}"}&lt;br /&gt;  ItemSubclasses=Hash.new {|h,k| raise "Unknown subclass #{k}"}&lt;br /&gt;  ItemHierarchy = [ItemClasses,ItemSubclasses]&lt;br /&gt;  ItemClasses[nil]=ItemSubclasses[nil]=nil&lt;br /&gt;  CategoryInfo.each_with_index {|line,i|&lt;br /&gt;    line =~ /^( *)(\w.*?)(\s*)$/&lt;br /&gt;    ItemHierarchy[$1.length][$2]=i+1&lt;br /&gt;  }&lt;br /&gt;  Qualities = {&lt;br /&gt;    "common" =&gt; 1,&lt;br /&gt;    "white" =&gt; 1,&lt;br /&gt;    "uncommon" =&gt; 2,&lt;br /&gt;    "green" =&gt; 2,&lt;br /&gt;    "rare" =&gt; 3,&lt;br /&gt;    "blue" =&gt; 3,&lt;br /&gt;    "epic" =&gt; 4,&lt;br /&gt;    "purple" =&gt; 4&lt;br /&gt;  }&lt;br /&gt;  Sorting = {&lt;br /&gt;    "name" =&gt; 6,&lt;br /&gt;    "level" =&gt; 2,&lt;br /&gt;    "time" =&gt; 3,&lt;br /&gt;    "seller" =&gt; 7,&lt;br /&gt;    "bid" =&gt; 4,&lt;br /&gt;    "price" =&gt; 4&lt;br /&gt;  }&lt;br /&gt;  Order = {&lt;br /&gt;    "ascending" =&gt; 0,&lt;br /&gt;    "asc" =&gt; 0,&lt;br /&gt;    "desc" =&gt; 1,&lt;br /&gt;    "descending" =&gt; 1&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  # Represents an AH to query. &lt;br /&gt;  class AuctionHouse&lt;br /&gt;    # e.g. AuctionHouse.new("Eldre'Thalas","Alliance")&lt;br /&gt;    # Neutral AH are not available&lt;br /&gt;    # Third parameter is 'EU' if you're on a european server.&lt;br /&gt;    def initialize(realm,faction,locale="US")&lt;br /&gt;      @realm,@locale = realm,locale&lt;br /&gt;      @faction = case faction.to_s.downcase&lt;br /&gt;      when "alliance"&lt;br /&gt;        "Alliance"&lt;br /&gt;      when "horde"&lt;br /&gt;        "Horde"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    # Main entry point.&lt;br /&gt;    # Usage: query("item name", :opt1 =&gt; "value", ...)&lt;br /&gt;    # Item name can be nil. &lt;br /&gt;    # Options are:&lt;br /&gt;    #  :min     minimum level item&lt;br /&gt;    #  :max     maximum level item&lt;br /&gt;    #  :quality white/green/blue/purple minimum item quality&lt;br /&gt;    #  :type    weapon/armor/consumable/trade good/recipe etc&lt;br /&gt;    #  :subtype 1h axe/enchanting/soul bag etc - must have the correct type specified too&lt;br /&gt;    #  :seller  seller to search for&lt;br /&gt;    #  :sort    name/level/time/bid/seller sorting method&lt;br /&gt;    #  :order   asc/desc sort order&lt;br /&gt;    #  :page    which page of results (1-based). 10 returned at a time.&lt;br /&gt;    # Returns an array of Warcraft::Auctions.&lt;br /&gt;    def query(name, opts={})&lt;br /&gt;      params = {}&lt;br /&gt;      params["realm"] = "#@realm #@locale" #"Eldre'Thalas US"&lt;br /&gt;      params["faction"] = if @faction=="Alliance" then 1 else 2 end&lt;br /&gt;      params["ItemName"] = name&lt;br /&gt;      params["LevelStart"] = opts[:min] # Level range to filter by&lt;br /&gt;      params["LevelEnd"] = opts[:max]&lt;br /&gt;      params["Seller"] = opts[:seller]&lt;br /&gt;      params["Rarity"] = Qualities[opts[:quality]]&lt;br /&gt;      params["itemClassID"] = ItemClasses[opts[:type]] &lt;br /&gt;      params["itemSubClass"] = ItemSubclasses[opts[:subtype]] &lt;br /&gt;      params["pagenum"] = (opts[:page] || 1).to_s&lt;br /&gt;      params["invenTypeID"] = nil # think this is the slot for armor searches - e.g. armor -&gt; leather -&gt; *shoulder*. unimplemented.&lt;br /&gt;      params["sort_column"] = Sorting[opts[:sort]]&lt;br /&gt;      params["sort_order"] = Order[opts[:order]]&lt;br /&gt;      &lt;br /&gt;      querytext = MiniJSON::encode(params)&lt;br /&gt;      &lt;br /&gt;      puts "Query:\n#{querytext}" if $DEBUG&lt;br /&gt;      &lt;br /&gt;      retrieve_results(querytext)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def retrieve_results(querytext)&lt;br /&gt;      # We just post the JSON to the given URL and get a JSON encoded string back&lt;br /&gt;      req = Net::HTTP::Post.new("/AuctionInfo.asmx/getAuctionInfo")&lt;br /&gt;      req["Content-Type"]="application/json"&lt;br /&gt;      response = Net::HTTP.new("www.auctionwowhouse.com",80).start {|http| http.request(req,querytext)}&lt;br /&gt;      response.error! unless Net::HTTPSuccess === response&lt;br /&gt;      resulttext = response.body&lt;br /&gt;      resulttext = MiniJSON::decode(resulttext)&lt;br /&gt;      puts "Response:\n#{resulttext}" if $DEBUG&lt;br /&gt;      &lt;br /&gt;      parse_results(resulttext)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    # Web service returns a blob of ugly html, so we scrape the data out of it. Cover your eyes...&lt;br /&gt;    def parse_results(resulttext)&lt;br /&gt;      items = []&lt;br /&gt;&lt;br /&gt;      resulttext.scan(/&lt;TR style='height.*?&gt;(.*?)&lt;\/TR&gt;/) {|text,| # A row of the main table containing an item&lt;br /&gt;        puts "Item:\n#{text}" if $DEBUG&lt;br /&gt;        item = Auction.new&lt;br /&gt;        text.scan(/&lt;font color=(.*?)&gt;(.*?)&lt;\/font&gt;/) {|color,name|&lt;br /&gt;          item.quality = color&lt;br /&gt;          item.name = name&lt;br /&gt;        }&lt;br /&gt;        text.scan(/&lt;td width="81" .*?&gt;(.*?)&lt;\/td&gt;/) {|name,|&lt;br /&gt;          item.seller = name&lt;br /&gt;        }&lt;br /&gt;        text.scan(/&lt;td width="75" .*?&gt;(.*?)&lt;\/td&gt;/) {|q,|&lt;br /&gt;          item.quantity = q.to_i&lt;br /&gt;        }&lt;br /&gt;    		text.scan(/&lt;td width="82" .*?&gt;&lt;div .*?&gt;(.*?)&lt;\/div&gt;&lt;\/td&gt;/) {|time,|&lt;br /&gt;    		  item.time = Duration.new(time.to_i)&lt;br /&gt;  		  }&lt;br /&gt;  		  text.scan(%r{(?:(\d+)&lt;img border=0 src="/images/wow_40_jin\.gif"&gt;)?(?:(\d+)&lt;img border=0 src="/images/wow_42_yin\.gif"&gt;)?(\d+)&lt;img border=0 src="/images/wow_44_tong\.gif"&gt;&lt;BR&gt;(?:(?:(\d+)&lt;img border=0 src="/images/wow_40_jin\.gif"&gt;)?(?:(\d+)&lt;img border=0 src="/images/wow_42_yin\.gif"&gt;)?(\d+)&lt;img border=0 src="/images/wow_44_tong\.gif"&gt;)?&lt;/a&gt;}) {|bg,bs,bc,g,s,c|&lt;br /&gt;          item.bid = Price.new(10000*bg.to_i + 100 * bs.to_i + bc.to_i)&lt;br /&gt;          item.buyout = Price.new(10000*g.to_i + 100 * s.to_i + c.to_i) if g #buyout may be absent&lt;br /&gt;        }&lt;br /&gt;        items &lt;&lt; item&lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      items&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  # Chunk of money (gold/silver/copper)&lt;br /&gt;  class Price &lt; DelegateClass(Integer); end unless Price # So we can reload the library without throwing&lt;br /&gt;  class Price&lt;br /&gt;    def gold; self / 10000; end&lt;br /&gt;    def silver; (self / 100)%100; end&lt;br /&gt;    def copper; self % 100; end&lt;br /&gt;    def to_a; [gold,silver,copper]; end&lt;br /&gt;    def inspect; "&lt;#{self.class} #{self.to_i}&gt;"; end&lt;br /&gt;    &lt;br /&gt;    # to display we pick the dominant unit (gold if it's at least 1g, else silver if it's at &lt;br /&gt;    # least 1s, else copper) and round to the nearest&lt;br /&gt;    def to_s&lt;br /&gt;      big,little,letter = &lt;br /&gt;        if gold &gt; 0&lt;br /&gt;          [gold,silver,"g"]&lt;br /&gt;        elsif silver &gt; 0&lt;br /&gt;          [silver,copper,"s"]&lt;br /&gt;        else&lt;br /&gt;          [copper,0,"c"]&lt;br /&gt;        end&lt;br /&gt;      big += 1 if little &gt;= 50&lt;br /&gt;      "#{big}#{letter}" &lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  # Measure of time (hours-minutes-seconds)&lt;br /&gt;  class Duration &lt; DelegateClass(Integer); end unless Duration&lt;br /&gt;  class Duration&lt;br /&gt;    def hours; self / 3600; end&lt;br /&gt;    def minutes; (self / 60)%60; end&lt;br /&gt;    def seconds; self % 60; end&lt;br /&gt;    def to_a; [hours,minutes,seconds] end&lt;br /&gt;    def to_s; "%d:%02d" % self.to_a; end # just show hours and minutes&lt;br /&gt;    def inspect; "&lt;#{self.class} #{to_s}&gt;"; end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  #Bare minimum JSON support to talk to the service&lt;br /&gt;  module MiniJSON&lt;br /&gt;    # Encode an object - we only support strings, numbers, and hashes&lt;br /&gt;    def self.encode(x)&lt;br /&gt;      case x&lt;br /&gt;      when String&lt;br /&gt;        "\"#{x.gsub('"','\\"')}\""&lt;br /&gt;      when Numeric&lt;br /&gt;        x.to_s&lt;br /&gt;      when Hash&lt;br /&gt;        "{" + x.map{|k,v| encode(k)+": "+encode(v)}.join(", ") + "}"&lt;br /&gt;      when NilClass&lt;br /&gt;        "\"\""&lt;br /&gt;      else&lt;br /&gt;        raise "Can't encode #{x.class}"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    # Decode an object - only supports a single string literal&lt;br /&gt;    def self.decode(x)&lt;br /&gt;      x.strip!&lt;br /&gt;      if x.length &gt; 1 and x[0]==?" and x[-1]==?" and x[-2]!=?\\&lt;br /&gt;        x[1..-2].gsub('\\"','"').gsub("\\'","'").gsub(/\\u([a-fA-F0-9]{4})/) {[$1.hex].pack("U") }&lt;br /&gt;      else&lt;br /&gt;        raise "Can't decode #{x.inspect}"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Fri, 13 Apr 2007 00:38:08 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/3811</guid>
      <author>tunah (Sam McCall)</author>
    </item>
  </channel>
</rss>
