forked from rikva/veilingbot
/
veilingbot.py
executable file
·269 lines (214 loc) · 9.64 KB
/
veilingbot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#!/usr/bin/env python
import sched
import time
import datetime
import sys
import traceback
import sqlite3
from selenium.common.exceptions import WebDriverException
from credentials import MY_NAME as VV_NAME
from tv_credentials import MY_NAME as TV_NAME
from ticketveiling import TicketVeiling
from vakantieveilingen import VakantieVeilingen
from veilingbotcore import log, start_browser, close_cookie_dialogs, ravenclient, make_screenshot, RAVEN_ENABLED
scheduler = sched.scheduler(time.time, time.sleep)
def begin(url):
try:
if "vakantieveilingen.nl" in url:
log("Using VakantieVeilingen engine")
Site = VakantieVeilingen
elif "ticketveiling.nl" in url:
log("Using TicketVeiling engine")
Site = TicketVeiling
else:
print "Cannot detect URL"
sys.exit(1)
SITE = None # So we can check if var has been initialized below
browser = start_browser(url, browser=USE_BROWSER)
SITE = Site(browser=browser, max_price=max_price, action=ACTION)
log("Remaining seconds: %s" % SITE.get_remaining_secs())
if SITE.get_remaining_secs() is not None and SITE.get_remaining_secs() > 200:
wait_secs = SITE.get_remaining_secs()-200
datetime_of_next_action = datetime.datetime.now() + datetime.timedelta(seconds=wait_secs)
log("Remaining seconds: More than 120 secs: '%s'. Scheduling a restart in '%s' seconds" %
(SITE.get_remaining_secs(), wait_secs))
log("This would be around %s" % datetime_of_next_action)
scheduler.enter(wait_secs, 0, begin, (url,))
browser.quit()
elif SITE.get_remaining_secs() is not None and SITE.get_remaining_secs() > 0:
# Only login when the current bid is below our max price.
if SITE.get_current_bid() < max_price:
log("Current bid (%s) is lower than our max price (%s); logging in"
% (SITE.get_current_bid(), max_price))
login = True
else:
log("Not logging in; current bid (%s) is higher than, or equal to, our max price (%s)."
%(SITE.get_current_bid(), max_price))
login = False
if login and not SITE.do_login():
scheduler.enter(0, 1, begin, (url,))
else:
# Close cookie dialog that might have re-appeared after signing in
close_cookie_dialogs(browser)
while SITE.get_remaining_secs() > 0:
global _current_bid
global _latest_bidder
global _remaining_secs
# Used to check if current bid has changed
prev_bid = _current_bid
_remaining_secs = SITE.get_remaining_secs()
_current_bid = SITE.get_current_bid()
_latest_bidder = SITE.get_latest_bidder()
if prev_bid != _current_bid and _current_bid != 0 and prev_bid is not None:
sys.stdout.write("\n")
sys.stdout.flush()
log("User '%s' just raised the bid to '%s' on %s seconds left."
% (_latest_bidder, _current_bid, _remaining_secs))
if _remaining_secs < 6 and _current_bid < max_price:
we_won = brute_force_bid(SITE, max_price)
if we_won:
log("Exiting!")
browser.quit()
sys.exit(0)
time.sleep(0.5)
else:
# The auction seems to be ended
time.sleep(5)
_current_bid = SITE.get_current_bid()
_latest_bidder = SITE.get_latest_bidder()
log("Auction has ended, winning bid is '%s' by '%s'." % (_current_bid, _latest_bidder))
save_winning_bid_and_log_history(bid=_current_bid, bidder=_latest_bidder)
browser.quit()
scheduler.enter(5, 1, begin, (url,))
elif SITE.get_remaining_secs() in (None, 0):
log('Auction seems to be closed. Scheduling restart in 60 secs.')
scheduler.enter(60, 1, begin, (url,))
else:
log('This should not happen.')
except WebDriverException as e:
#log("Caught WebDriverException, the browser probably crashed. Forcing browser quit and rescheduling restart in 10 seconds.")
#log("The exception was: '%s'" % e)
#traceback.print_exc()
log("Caught WebDriverException. Trying a refresh.")
if SITE is None:
log("Nope, SITE is None - capturing exception")
if RAVEN_ENABLED:
ravenclient.captureException()
log("Scheduling a restart")
scheduler.enter(15, 1, begin, (url,))
return
try:
SITE.browser.refresh()
except:
if RAVEN_ENABLED:
ravenclient.captureException()
log("That went wrong. Restarting browser")
SITE.browser.quit()
scheduler.enter(15, 1, begin, (url,))
except Exception as e:
log("Caught unexpected exception: '%s'. Forcing browser quit and rescheduling restart in 60 seconds." % e.message)
log("The exception was: '%s'" % e)
traceback.print_exc()
if RAVEN_ENABLED:
ravenclient.captureException()
try:
SITE.browser.quit()
except: pass
scheduler.enter(60, 1, begin, (url,))
def _get_winning_bids(auction_id):
CURSOR.execute("select datetime, name, amount from winningbid where auction = %s;" % auction_id)
result = CURSOR.fetchall()
winning_bids = []
for r in result:
winning_bids.append(r[2])
return winning_bids
def save_winning_bid_and_log_history(bid, bidder):
timestring = int(time.time())
CURSOR.execute("insert into winningbid values (%s, %s, '%s', %s);"
% (AUCTION_ID, timestring, bidder, bid))
DBCONN.commit()
winning_bids = _get_winning_bids(AUCTION_ID)
lowest_bid = sorted(winning_bids)[0]
highest_bid = sorted(winning_bids)[-1]
log("Lowest bid: %s | Highest bid: %s" % (lowest_bid, highest_bid))
def brute_force_bid(site, max_price):
"""
Try to win the auction with the lowest bid, under max_price.
Automatically over-bids other bidders.
Always increments the current bid with one.
Returns True if we won
Returns False if we lost
Always assumes that we won, we need to CHECK if we may have lost.
"""
log('Starting brute force bid with a max price of %s' % max_price)
my_last_bid = 0
_current_bid = None
while site.get_remaining_secs() > 0:
__current_bid_last_time = _current_bid
_current_bid = site.get_current_bid()
log("DEBUG: Still in brute force bid mode. Current bid: %d | my last bid: %d" % (_current_bid, my_last_bid))
if _current_bid != __current_bid_last_time:
_latest_bidder = site.get_latest_bidder()
_remaining_secs = site.get_remaining_secs()
log("User '%s' just raised the bid to '%s' on %s seconds left."
% (_latest_bidder, _current_bid, _remaining_secs))
if _current_bid > my_last_bid and _current_bid < max_price:
my_last_bid = _current_bid+1
if my_last_bid <= max_price:
log("Placing bid of %s" % my_last_bid)
site.place_bid(my_last_bid)
else:
log("Current bid is higher than or equal to my max price")
return False
#time.sleep(0.1)
# First, create a screenshot
make_screenshot(site.browser)
# We assume that we have won! But
# Let's check if we have lost
# Wait a few seconds
log("Checking if we've lost")
time.sleep(3)
winning_bidder = site.get_latest_bidder()
last_bid = site.get_current_bid()
log("Winning bidder: '%s'" % winning_bidder)
log("Winning bid: '%s'" % last_bid)
# Double confirm that we have lost, cause it means that we will begin bidding again.
if winning_bidder not in (VV_NAME, TV_NAME) and last_bid != my_last_bid:
# Too bad, it sure looks like we lost
log("Too bad, we lost")
return False
# Wait... we have won!
log("It's possible that we've won.")
return True
if __name__ == '__main__':
URL = sys.argv[1]
max_price = int(sys.argv[2])
USE_BROWSER = sys.argv[3]
ACTION = sys.argv[4]
# used for checking if bid has changed
_current_bid = None
DBCONN = sqlite3.connect('veilingbot.db')
CURSOR = DBCONN.cursor()
CURSOR.execute("create table if not exists auction ("
"id integer primary key autoincrement not null, "
"url text);")
CURSOR.execute("create table if not exists winningbid ("
"auction integer, "
"datetime integer, "
"name text, "
"amount integer, "
# "winning integer, "
"foreign key(auction) references auctions(id));")
CURSOR.execute("select id from auction where url = '%s'" % URL)
result = CURSOR.fetchone()
if not result:
CURSOR.execute("insert into auction values (NULL, '%s');" % URL)
DBCONN.commit()
CURSOR.execute("select id from auction where url = '%s'" % URL)
result = CURSOR.fetchone()
AUCTION_ID = result[0]
winning_bids = _get_winning_bids(AUCTION_ID)
scheduler.enter(0, 1, begin, (URL,))
log('Starting scheduler')
scheduler.run()
log('Scheduler finished')