123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- #!/usr/bin/env ruby
- require 'uri'
- require 'cgi'
- require 'net/https'
- require 'gixen_error'
- begin
- require 'to_query'
- rescue LoadError => err
- require 'active_support'
- require 'active_support/all'
- end
- class Gixen
- CORE_GIXEN_URL='https://www.gixen.com/api.php' #:nodoc:
- #:nodoc:
- LISTING_FORMAT = [:break,
- :itemid,
- :endtime,
- :maxbid,
- :status,
- :message,
- :title,
- :snipegroup,
- :quantity,
- :bidoffset]
- # Create a Gixen object for interacting with the user's Gixen
- # account, placing snipes, deleting snipes, and determining what
- # snipes have been set up.
- #
- # [+user+] an eBay username
- # [+pass+] an eBay password
- # [+validate_ssl+] true requests validation against the gixen cert file, false presumes the SSL is valid. Defaults to false.
- #
- # Gixen uses eBay authentication for its users, so it doesn't have
- # to have a different user/pass for its users.
- def initialize(user, pass, validate_ssl = false)
- @username = user
- @password = pass
- @validate_ssl = validate_ssl
- end
- private
- def gixen_url
- "#{CORE_GIXEN_URL}?username=#{@username}&password=#{@password}¬ags=1"
- end
- def submit(params)
- url = "#{gixen_url}&#{params.to_query}"
- uri = URI.parse(url)
- http = Net::HTTP.new(uri.host, uri.port)
- http.use_ssl = true if uri.scheme == 'https' # enable SSL/TLS
- if @validate_ssl
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
- http.ca_file = pem_file
- else
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
- end
- http.get("#{uri.path}?#{uri.query}")
- end
- def pem_file
- File.expand_path(File.dirname(__FILE__) + "/gixen.pem")
- end
- def parse_response(resp)
- data = resp.body
- data.strip! if data
- if data =~ /^ERROR \(([0-9]+)\): (.*)$/
- error_code = $1
- error_text = $2
- raise GixenError.new(error_code.to_i, error_text)
- end
- data
- end
- def handle_snipes_list(body)
- body.split("\n").inject([]) do |accum, line|
- line.strip!
- hash = {}
- line.split("|#!#|").each_with_index do |entry, index|
- if index < LISTING_FORMAT.length
- hash[LISTING_FORMAT[index]] = entry
- else
- hash[index] = entry
- end
- end unless line =~ %r%^<br.*/>OK M(AIN|IRROR) LISTED$%
- hash.empty? ? accum : (accum << hash)
- end
- end
- def sourcify_hashes(hash_list, mirror = false)
- hash_list.each do |h|
- h[:mirror] = mirror
- end
- end
- public
- # Place a snipe on an +item+ (the auction item #) for +bid+ (string amount).
- # [+item+] the item number of the listing on eBay
- # [+bid+] a string amount of currency-neutral money, for example "23.50". The currency bid in will be the currency of the listing
- # [+options+] A collection of optional parameters for setting snipe meta-data.
- # Optional parameters include:
- # * <tt>:snipegroup => <i>group number</i></tt>, e.g. <tt>:snipegroup => 1</tt> (default: 0, no groups used)
- # * <tt>:quantity => <i>number</i></tt> (default: 1, single item auction) <b>[_obsolete_]</b>
- # * <tt>:bidoffset => <i>seconds before end</i></tt> (3, 6, 8, 10 or 15. Default value is 6)
- # * <tt>:bidoffsetmirror => <i>seconds before end</i></tt> (same as above, just for mirror server)
- #
- # @return [boolean] true if the snipe was successfully set, false if
- # something other than 'OK ADDED' came back, and throws a GixenError
- # if there is any server-side problem.
- def snipe(item, bid, options = {})
- response = submit({:itemid => item, :maxbid => bid}.merge(options))
- body = parse_response(response).strip
- !!(body =~ /^OK #{item} ADDED$/)
- end
- # Remove a snipe from an +item+ (the auction item #).
- #
- # @return [boolean] true if the snipe was successfully deleted, false if
- # something other than 'OK DELETED' came back, and throws a GixenError
- # if there is any server-side problem.
- def unsnipe(item)
- response = submit({:ditemid => item})
- body = parse_response(response).strip
- !!(body =~ /^OK #{item} DELETED$/)
- end
- # Lists all snipes set, skipped, or done on any Gixen server.
- #
- # @return [array of hashes] an array where each entry is a hash
- # containing all the info for a given snipe, an empty array if no
- # auctions are listed on either the main Gixen server or the Gixen
- # Mirror server.
- #
- # An additional field is added, :mirror, which is either true or
- # false, depending on if the item hash came from the mirror server
- # or not.
- #
- # It raises a GixenError if there is a server-side problem.
- def snipes
- sourcify_hashes(main_snipes) + sourcify_hashes(mirror_snipes, true)
- end
- # Lists all snipes currently set set, skipped, or done on Gixen's main server.
- #
- # @return [array of hashes] an array where each entry is a hash
- # containing all the info for a given snipe, an empty array if none
- # are listed on the main Gixen server. It raises a GixenError if
- # there is a server-side problem.
- def main_snipes
- response = submit({:listsnipesmain => 1})
- body = parse_response(response)
- handle_snipes_list(body)
- end
- # List all snipes currently set set, skipped, or done on Gixen's mirror server.
- #
- # @return [array of hashes] an array where each entry is a hash
- # containing all the info for a given snipe, an empty array if none
- # are listed on the Gixen Mirror server. It raises a GixenError if
- # there is a server-side problem.
- def mirror_snipes
- response = submit({:listsnipesmirror => 1})
- body = parse_response(response)
- handle_snipes_list(body)
- end
- # Normally the snipes that are completed are still listed when
- # retrieving snipes from the server; this method clears completed
- # listings, so the list of snipes is just active snipes.
- #
- # @return [boolean] true if the purge completed successfully. It
- # raises a GixenError if there is a server-side problem.
- def purge
- response = submit({:purgecompleted => 1})
- body = parse_response(response).strip
- !!(body =~ /^OK COMPLETEDPURGED$/)
- end
- end
|