auto-bidder 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 hoverlover
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,29 @@
1
+ # AutoBidder: Win without trying
2
+
3
+ This library places bids on online auction sites for you automatically while you do other important things, like walking your dog.
4
+
5
+ ## Installing
6
+
7
+ gem install auto-bidder
8
+
9
+ ## Usage
10
+
11
+ AutoBidder is intended to be ran from the command line:
12
+
13
+ Chad-Boyds-iMac:~ cboyd$ auto-bidder --help
14
+ Usage: auto-bidder [options] URL
15
+ -v, --verbose Be more verbose
16
+ -x, --max-price PRICE Maximum price you are willing to pay for the auction item
17
+ -n, --min-price [PRICE] Minimum price the auction item must reach before bidding starts
18
+ -c, --bid-cost COST How much each bid actually costs you
19
+ -u, --username USER Username of your account for the auction site
20
+ -s, --bid-seconds [SECONDS] How many seconds remaining on the clock before a bid is placed
21
+ -h, --help Display this screen
22
+
23
+ It is a gem, though. So I suppose you could use it within your own project.
24
+
25
+ AutoBidder works by utilizing [Watir](http://watir.com), an open-source library for automating web browsers. The AutoBidder library will launch a Safari window and connect to the URL you supply on the command line.
26
+
27
+ ## Notes
28
+
29
+ **Watir** supports IE, Firefox, Safari and Chrome. AutoBidder uses Safari only, however. Feel free to fork this project and implement any or all of the other browsers if you wish.
data/bin/auto-bidder ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'auto_bidder'
@@ -0,0 +1,115 @@
1
+ require 'safariwatir'
2
+ require 'optparse'
3
+ require 'auto_bidder/auction_interface'
4
+ require 'auto_bidder/bigdeal'
5
+ require 'auto_bidder/browser_interface'
6
+ require 'logger'
7
+ require 'uri'
8
+
9
+ class AutoBidder
10
+ def initialize(url, options)
11
+ @num_bids = 0
12
+
13
+ # Glean the TLD name from the host and use it to create the auction interface
14
+ host = URI.parse(url).host
15
+ begin
16
+ @auction_interface = Module.const_get(host.split(".")[-2].capitalize).new(url, options)
17
+ rescue NameError
18
+ $log.fatal "Auction site #{host} not supported"
19
+ end
20
+ end
21
+
22
+ def start
23
+ @auction_interface.connect_to_auction
24
+ @auction_interface.start_monitoring
25
+ end
26
+ end
27
+
28
+ def parse_command_line
29
+ # This hash will hold all of the options
30
+ # parsed from the command-line by
31
+ # OptionParser.
32
+ options = {}
33
+
34
+ optparse = OptionParser.new do|opts|
35
+ # Set a banner, displayed at the top
36
+ # of the help screen.
37
+ opts.banner = "Usage: auto-bidder [options] URL"
38
+
39
+ # Define the options, and what they do
40
+ options[:verbose] = false
41
+ opts.on('-v', '--verbose', 'Be more verbose') do
42
+ options[:verbose] = true
43
+ end
44
+
45
+ opts.on("-x", "--max-price PRICE", Float, 'Maximum price you are willing to pay for the auction item') do |max_price|
46
+ options[:max_price] = max_price
47
+ end
48
+
49
+ opts.on("-n", "--min-price [PRICE]", Float, 'Minimum price the auction item must reach before bidding starts') do |min_price|
50
+ options[:min_price] = min_price || 0.0
51
+ end
52
+
53
+ opts.on("-c", "--bid-cost COST", Float, 'How much each bid actually costs you') do |bid_cost|
54
+ options[:bid_cost] = bid_cost
55
+ end
56
+
57
+ opts.on("-u", "--username USER", 'Username of your account for the auction site') do |username|
58
+ options[:username] = username
59
+ end
60
+
61
+ opts.on("-s", "--bid-seconds [SECONDS]", Integer, 'How many seconds remaining on the clock before a bid is placed') do |bid_secs|
62
+ options[:bid_secs] = bid_secs || 1
63
+ end
64
+
65
+ # This displays the help screen, all programs are
66
+ # assumed to have this option.
67
+ opts.on( '-h', '--help', 'Display this screen' ) do
68
+ puts opts
69
+ exit
70
+ end
71
+ end
72
+
73
+ # Parse the command-line. Remember there are two forms
74
+ # of the parse method. The 'parse' method simply parses
75
+ # ARGV, while the 'parse!' method parses ARGV and removes
76
+ # any options found there, as well as any parameters for
77
+ # the options. What's left is the list of files to resize.
78
+ optparse.parse! ARGV
79
+
80
+ ensure_option_is_present options, :max_price, optparse
81
+ ensure_option_is_present options, :bid_cost, optparse
82
+ ensure_option_is_present options, :username, optparse
83
+
84
+ url = ARGV[0]
85
+ unless url
86
+ puts "URL must be given"
87
+ puts optparse
88
+ exit 1
89
+ end
90
+
91
+ ARGV.clear
92
+
93
+ if options[:username]
94
+ print "Auction site password:"
95
+ options[:password] = gets.chomp
96
+ end
97
+
98
+ $log = Logger.new(STDOUT)
99
+ $log.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
100
+
101
+ $log.debug "Verbose logging turned on" if $log.debug?
102
+
103
+ [url, options]
104
+ end
105
+
106
+ def ensure_option_is_present(options, name, parser)
107
+ if !options.has_key? name
108
+ puts "Option #{name} is required"
109
+ puts parser
110
+ exit 1
111
+ end
112
+ end
113
+
114
+ url, options = parse_command_line
115
+ AutoBidder.new(url, options).start
@@ -0,0 +1,112 @@
1
+ module AuctionInterface
2
+
3
+ def start_monitoring
4
+ while true do
5
+ reload_if_necessary
6
+
7
+ @hours = hours
8
+ @mins = mins
9
+ @secs = secs
10
+ print_stats
11
+
12
+ if @hours == 0 && @mins == 0 && @secs == @bid_secs && ok_to_bid
13
+ place_bid
14
+ sleep 10
15
+ end
16
+ end
17
+ end
18
+
19
+ # --------------- #
20
+ # Private methods #
21
+ # --------------- #
22
+ def ok_to_bid
23
+ current_price = get_current_price
24
+ current_price >= @min_price && ((@num_bids + 1) * @bid_cost + current_price) <= @max_price
25
+ end
26
+
27
+ def reload_if_necessary
28
+ # Reload every 8-10 minutes so the website doesn't think we've gone idle. If @secs is 5 or less
29
+ # put off the reload so we don't accidentally miss a bid opportunity.
30
+ if (Time.now.to_i - @last_reload) / 60 >= @next_reload_in && @secs > 10
31
+ @browser.reload
32
+ recalculate_next_reload_time
33
+ end
34
+ end
35
+
36
+ def hours
37
+ begin
38
+ get_hours
39
+ rescue
40
+ $log.error "Could not find hours element"
41
+ @browser.reload
42
+ -1
43
+ end
44
+ end
45
+
46
+ def mins
47
+ begin
48
+ get_mins
49
+ rescue
50
+ $log.error "Could not find minutes element"
51
+ @browser.reload
52
+ -1
53
+ end
54
+ end
55
+
56
+ def secs
57
+ begin
58
+ get_secs
59
+ rescue
60
+ $log.info "Could not find seconds element"
61
+ @browser.reload
62
+ -1
63
+ end
64
+ end
65
+
66
+ def recalculate_next_reload_time
67
+ @next_reload_in = (rand(3) + 8)
68
+ end
69
+
70
+ def connect_to_auction
71
+ sign_in unless signed_in?
72
+
73
+ @last_reload = Time.now.to_i
74
+ recalculate_next_reload_time
75
+ end
76
+
77
+ def place_bid
78
+ $log.info <<-BIDDING
79
+ \n=======================================
80
+ PLACING BID
81
+ #{Time.now}
82
+ =======================================
83
+ BIDDING
84
+
85
+ begin
86
+ execute_bid
87
+ @num_bids += 1
88
+
89
+ # The server knows we're still here because we just placed a bid, so reset the next reload time.
90
+ recalculate_next_reload_time
91
+ rescue
92
+ $log.error "Error while placing bid!"
93
+ end
94
+ end
95
+
96
+ def print_stats
97
+ @last_debug ||= Time.now.to_i
98
+
99
+ # print debug every 10 seconds
100
+ if Time.now.to_i - @last_debug > 10
101
+ begin
102
+ $log.debug %(#{Time.now.strftime "%m/%d/%Y %H:%M:%S"} - #{"%02d" % @hours}:#{"%02d" % @mins}:#{"%02d" % @secs}, #{get_formatted_price})
103
+ rescue => e
104
+ $log.error "Could not print debug statement\n#{e.backtrace.join("\n")}"
105
+ end
106
+ @last_debug = Time.now.to_i
107
+ end
108
+
109
+ sleep 0.01
110
+ end
111
+ private :ok_to_bid, :reload_if_necessary, :hours, :mins, :secs, :recalculate_next_reload_time, :place_bid, :print_stats
112
+ end
@@ -0,0 +1,53 @@
1
+ class Bigdeal
2
+ include AuctionInterface
3
+
4
+ def initialize(url, options)
5
+ @auction_url = url
6
+ @auction_id = @auction_url.match(/([\d]+\Z)/)[0]
7
+ @browser = BrowserInterface.new @auction_url
8
+
9
+ @max_price = options[:max_price]
10
+ @min_price = options[:min_price]
11
+ @bid_cost = options[:bid_cost]
12
+ @bid_secs = options[:bid_secs]
13
+ @username = options[:username]
14
+ @password = options[:password]
15
+
16
+ @num_bids = 0
17
+ end
18
+
19
+ def get_current_price
20
+ @browser.span(:id, "#{@auction_id}-price").text.delete('$').to_f
21
+ end
22
+
23
+ def signed_in?
24
+ @browser.link(:text, /Logout/).exists?
25
+ end
26
+
27
+ def sign_in
28
+ @browser.link(:text, /Login/).click
29
+ @browser.text_field(:id, "userNameOrEmail").set(@username)
30
+ @browser.text_field(:id, "password").set(@password)
31
+ @browser.form(:action, /login/).submit
32
+ end
33
+
34
+ def get_hours
35
+ @browser.span(:id, "#{@auction_id}-hours_remaining").text.to_i
36
+ end
37
+
38
+ def get_mins
39
+ @browser.span(:id, "#{@auction_id}-minutes_remaining").text.to_i
40
+ end
41
+
42
+ def get_secs
43
+ @browser.span(:id, "#{@auction_id}-seconds_remaining").text.to_i
44
+ end
45
+
46
+ def execute_bid
47
+ @browser.div(:id, "#{@auction_id}-bid_button").click
48
+ end
49
+
50
+ def get_formatted_price
51
+ @browser.span(:id, @auction_id + "-price").text
52
+ end
53
+ end
@@ -0,0 +1,26 @@
1
+ class BrowserInterface < Watir::Safari
2
+
3
+ def initialize(url)
4
+ super()
5
+ @url = url
6
+
7
+ begin
8
+ goto @url
9
+ rescue
10
+ $log.error "Failed to load website. Will retry."
11
+ retry
12
+ end
13
+ end
14
+
15
+ def reload
16
+ $log.debug "reloading ..."
17
+ begin
18
+ goto @url
19
+ @last_reload = Time.now.to_i
20
+ sleep 5 # give the browser time to finish loading before continuing
21
+ rescue
22
+ $log.error "Failed to reload. Will retry."
23
+ retry
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Autobidder" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'autobidder'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: auto-bidder
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - hoverlover
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-09 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: gem for automatically placing bids on auction sites
23
+ email: hoverlover@gmail.com
24
+ executables:
25
+ - auto-bidder
26
+ - auto-bidder
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - LICENSE
31
+ - README.markdown
32
+ files:
33
+ - bin/auto-bidder
34
+ - lib/auto_bidder.rb
35
+ - lib/auto_bidder/auction_interface.rb
36
+ - lib/auto_bidder/bigdeal.rb
37
+ - lib/auto_bidder/browser_interface.rb
38
+ - LICENSE
39
+ - README.markdown
40
+ - spec/auto_bidder_spec.rb
41
+ - spec/spec_helper.rb
42
+ has_rdoc: true
43
+ homepage: http://github.com/hoverlover/auto-bidder
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --charset=UTF-8
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ hash: 3
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ requirements: []
70
+
71
+ rubyforge_project:
72
+ rubygems_version: 1.3.7
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: gem for automatically placing bids on auction sites
76
+ test_files:
77
+ - spec/auto_bidder_spec.rb
78
+ - spec/spec_helper.rb