Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
rschultheis committed Sep 2, 2011
0 parents commit cbd5929
Show file tree
Hide file tree
Showing 9 changed files with 514 additions and 0 deletions.
30 changes: 30 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Penny Auction Observer can record penny auctions taking place on penny auction websites.

How the F do you use this thing?

ruby bin/pa_auction_bot.rb -- help


What the F does it do?

I "observes" a live penny auction, and executes blocks of ruby code at the key events.
The blocks of ruby code are stored in interchangeable hook files.
The key events are:
- Monitoring a new auction
- New bids occur on a live auction
- A timer threshold is reached
- The auction ends


What the F is it useful for?

- Creating logs of penny auctions
- Creating bots which can bid on penny auctions (not recommended by the authors of this tool)
- Doing research on penny auctions


Why the F did you make it?

Penny auctions are an interesting thing.
In my opinion they exploit weaknesses in the human psyche, much the same way gambling does.
I wanted to collect data and study this phenomenon, strictly for my own fun.
56 changes: 56 additions & 0 deletions bin/pa_auction_bot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require 'optparse'
#require 'rubygems'


#Deal with the cmd line
def parse_cmd_line
options = {
:hooks_file => 'bots/csv_logger.rb',
:site => 'QUIBIDS',
}

optparse = OptionParser.new do |opts|
opts.banner = %Q|
Penny Auction Observer
example: ruby -I lib bin/pa_auction_but.rb bots/csv_logger.rb
|

opts.on("-b", "--bot-file=FILE_NAME",
"specify the bot file to execute, default is '#{options[:hooks_file]}'") { |filename| options[:hooks_file] = filename}

opts.on("-a", "--auction-id=AUCTION_ID",
"the auction id") { |id| options[:auction_id] = id }

opts.on("-s", "--site=SITE",
"Specify the site to use, default is '#{options[:site]}'") { |site| options[:site] = site}


end
optparse.parse!
options
end
options = parse_cmd_line



require 'pa_site'
require 'pa_observer'

#setup the object
auction_id = options[:auction_id]
load options[:hooks_file]
pa_site = QB_Site.new options[:site].upcase
pa_site.start auction_id



auction_observer = QB_Observer.new pa_site


auction_observer.hooks[:on_new_bids] = OnNewBids
auction_observer.hooks[:on_new_auction] = OnNewAuction
auction_observer.hooks[:on_auction_end] = OnAuctionEnd
auction_observer.hooks[:on_timer_threshold] = OnTimerThreshold

auction_observer.observe_auction
38 changes: 38 additions & 0 deletions bots/basic_watcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## Logs a QuiBids Auction to STDOUT
#

## NEW BIDS
#data
num_bids = 0
last_amt = -1.0

## AUCTION HOOKS
OnNewAuction = lambda {|auction_name|
last_amt = -1.0
num_bids = 0
puts "Now watching new auction: #{auction_name}"
}

OnAuctionEnd = lambda {|auction_name|
puts "Done watching auction: #{auction_name}"
}


OnNewBids = lambda {|new_bids|
new_bids.each do |bid|
num_bids += 1
puts "NEW BID: '#{bid[:bidder]}' : '#{bid[:amt]}' : '#{bid[:type]}' : #{bid[:last_secs]} : #{last_amt}"
end
last_amt = new_bids.last[:amt] if new_bids.count > 0
puts "Processed #{new_bids.count} new bids"
}



## TIMER THRESHOLD
num_hits = 0
OnTimerThreshold = lambda {|secs, browser|
#browser.bid
num_hits += 1
puts "TIMER THRESHOLD HIT: #{num_hits} so far"
}
48 changes: 48 additions & 0 deletions bots/bidder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
## Logs a QuiBids Auction to CSV
#

require 'bidder_model'

## NEW BIDS
#data
num_bids = 0
num_skips = 0
last_amt = -1.0


## AUCTION HOOKS
OnNewAuction = lambda {|auction_name|

@model = QB_Model.new
puts "Initialized '#{auction_name}'"
}

OnAuctionEnd = lambda {|auction_name|
puts "Auction End"
}


OnNewBids = lambda {|new_bids|

@model.process_new_bids new_bids


}



## TIMER THRESHOLD
num_bids = 0
OnTimerThreshold = lambda {|secs, browser|
if @model.would_bid
#browser.bid
num_bids += 1
puts "TIMER HIT: #{num_bids} so far"
print "\a"
else
num_skips += 1
puts "SKIP: #{num_skips} so far"
end
}


69 changes: 69 additions & 0 deletions bots/bidder_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

class QB_Model
def initialize
@bids = []
@bidders = {}

@num_bids = 0
@last_amt = 0.00

@uniques = {:u10 => 10, :u20 => 20}
@autoq = 9999
end

def process_new_bids new_bids
new_bids.each do |bid|

@bids << bid

if @bidders.has_key? bid[:bidder]
@bidders[bid[:bidder]][:count] += 1
else
@bidders[bid[:bidder]] = {
:count => 1
}
end

@num_bids += 1
puts "NEW BID #{@num_bids}\t #{bid[:bidder]}\t #{bid[:amt]}\t #{bid[:type]}\t #{bid[:last_secs]}\t:: #{@bidders[bid[:bidder]][:count]} so far"
end

##determine unique bidders
@uniques = {}
bidders = @bids.reverse.map{|b| b[:bidder]}
@uniques[:u10] = bidders[0, 10].uniq.length
@uniques[:u20] = bidders[0, 20].uniq.length
#@uniques[:u30] = bidders[0, 30].uniq.length
#@uniques[:u40] = bidders[0, 40].uniq.length
#@uniques[:u50] = bidders[0, 50].uniq.length

puts "Uniques: #{@uniques.inspect}"


##determine ratio of Auto to Manual
types = @bids.reverse.map{|b| b[:type]}

@autoq = 0
plus = 10
while (plus > 0)
@autoq += plus if types[10-plus] == :automatic
plus -= 1
end

puts "AUTO Q: #{@autoq}"

@last_amt = new_bids.last[:amt] if new_bids.length > 0
puts "Processed #{new_bids.length} new bids"
end

def would_bid

return false if @num_bids < 25
return false if @uniques[:u10] > 6
return false if @uniques[:u20] > 9
return false if @autoq > 30

return true
end
end

55 changes: 55 additions & 0 deletions bots/csv_logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
## Logs a QuiBids Auction to CSV
#

## NEW BIDS
#data
num_bids = 0
last_amt = -1.0

### HELPERS
def init_csv filename
filename = 'csv/' + filename + '.' + Time.now.strftime("%m%d%Y_%H%M") + '.csv'
@csv_writer = File.open(filename, 'w')
puts "Opened csv '#{filename}'"
end

def write_csv line_array
str = ""
line_array.each {|f| str += "#{f.to_s}, "}
@csv_writer.puts str.sub(/, $/,'')
end


## AUCTION HOOKS
OnNewAuction = lambda {|auction_name|
last_amt = -1.0
num_bids = 0
init_csv auction_name
}

OnAuctionEnd = lambda {|auction_name|
@csv_writer.close
puts "Closed csv"
}


OnNewBids = lambda {|new_bids|
new_bids.each do |bid|
num_bids += 1
puts "NEW BID: '#{bid[:bidder]}' : '#{bid[:amt]}' : '#{bid[:type]}' : #{bid[:last_secs]} : #{last_amt}"
write_csv [num_bids, Time.now.strftime("%H:%M:%S"), bid[:amt], bid[:bidder], bid[:type], bid[:last_secs], last_amt]
end
last_amt = new_bids.last[:amt] if new_bids.count > 0
puts "Processed #{new_bids.count} new bids"
}



## TIMER THRESHOLD
num_hits = 0
OnTimerThreshold = lambda {|secs, browser|
#browser.bid
num_hits += 1
puts "TIMER HIT: #{num_hits} so far"
#print "\a"
}
86 changes: 86 additions & 0 deletions lib/pa_observer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# This class watches an auction

require 'pa_site'

class QB_Observer
def initialize pa_site
@pa_site = pa_site

@hooks = {} #contains lambdas which are called in observice auction loop
end

attr_accessor :hooks

def auction_name
@pa_site.auction_name
end

def process_event name, *args
@hooks[name].call(*args) if @hooks.has_key? name
end

def get_new_bids
enhanced_bids = @pa_site.get_new_bids
enhanced_bids.each {|b| b[:last_secs] = @last_secs }
enhanced_bids
end

def observe_auction
@pa_site.initialize_auction

process_event :on_new_auction, auction_name

#DATA USED IN LOOP
cur_secs = 0
@last_secs = -1
secs_since_refresh = 0

while ( true )
begin
cur_secs = @pa_site.seconds_left
rescue
puts $!
break
end

#If the timer changed since last observation
if (cur_secs != @last_secs )

#If the timer is almost out (bid)
if cur_secs < 2
process_event :on_timer_threshold, cur_secs, @pa_site
end

puts " - " + cur_secs.to_s

#If the timer went up, then we have new bids to process
if (cur_secs > @last_secs )
#Need to refresh the browser periodically to prevent inactivity popups
if secs_since_refresh > 900
@pa_site.refresh_auction
secs_since_refresh = 0
end

#keep getting new bids until there isn't any left to get, sometimes the time spent in new bid event is enough for new bids to show up... dont want to wait until another bid
new_bids = get_new_bids
while (new_bids.count > 0)
process_event :on_new_bids, new_bids
sleep 1.0
new_bids = get_new_bids
end

end

#refresh the browser periodically to prevent innactivity popups
secs_since_refresh += 1
end

@last_secs = cur_secs
sleep 0.05
end
puts "End of Auction"
process_event :on_auction_end, auction_name
end

end

Loading

0 comments on commit cbd5929

Please sign in to comment.