#!/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%^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:
# * :snipegroup => group number, e.g. :snipegroup => 1 (default: 0, no groups used)
# * :quantity => number (default: 1, single item auction) [_obsolete_]
# * :bidoffset => seconds before end (3, 6, 8, 10 or 15. Default value is 6)
# * :bidoffsetmirror => seconds before end (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