forked from spikeruk/TicketmasterBot
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathscript.js
More file actions
282 lines (237 loc) · 10.8 KB
/
script.js
File metadata and controls
282 lines (237 loc) · 10.8 KB
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
// ==UserScript==
// @name TicketMasterBuyV2
// @namespace http://tampermonkey.net/
// @version 0.4
// @description Fast execution of reserving tickets in cart
// @match https://www.ticketmaster.co.uk/*event/*
// @match https://www1.ticketmaster.co.uk/*event/*
// @match https://www.ticketmaster.com/*event/*
// @match https://www1.ticketmaster.com/*event/*
// @match https://concerts1.livenation.com/*event/*
// @match https://concerts1.livenation.com/*event/*
// @match https://www.ticketmaster.ie/*event/*
// @match https://www1.ticketmaster.ie/*event/*
// @require https://code.jquery.com/jquery-2.1.3.min.js
// @grant none
// ==/UserScript==
var refreshIntervalSecondsMin=1; //Set this to how often you want to check for tickets (Note: Do this too fast and TicketMaster may block your ip address)
var refreshIntervalSecondsMax=5;
var numberOfTickets=2; //Set this to the number of tickets you want. We should prob test out various amounts to see which amounts we should try for.
var publicIp; //To be overridden by bash script.
var ticketsChannel = "https://hooks.slack.com/services/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"
var buyChannel = "https://hooks.slack.com/services/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"
function sendToSlack(text, channelUrl = ticketsChannel) {
jQuery.ajax({
data: 'payload=' + JSON.stringify({
"text": text
}),
dataType: 'json',
processData: false,
type: 'POST',
url: channelUrl
});
}
function getPublicIP(func) {
console.log("getPublicIP")
jQuery.get("https://api.ipify.org?format=json", function(response) {
func(response.ip)
}, "jsonp")
}
function getAllCookieNames() {
var pairs = document.cookie.split(";");
return pairs.map(function(pair) {
return (pair.split("=")[0]+'').trim()
})
}
function deleteAllCookies() {
getAllCookieNames().map(function(cname) {
document.cookie = cname+'=;expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
})
}
function SkipPopup()
{
var popupPresent = getElementByXpath('//button[@class = "modal-dialog__button landing-modal-footer__skip-button"]');
if(popupPresent)
{
try{ popupPresent.click();}catch(ex){}
}
}
function CheckForFilterPanel(){
var filterBar = getElementByXpath('//div[@class = "filter-bar__content"]');
return filterBar;
}
function clickTicketTypeCheckbox(searchString) {
var selector = "li.checkbox-list__item.checkbox:contains('" + searchString + "')"
jQuery(selector).children('input').each(function(idx, elmt) {elmt.click()})
}
function clickClearFilters() {
var selector = "button:contains('Clear')"
jQuery(selector).each(function(idx, elmt) { elmt.click()})
}
function ProcessFilterPanel(filterBar){
// We can update this depending on group preferences.
// For now this:
// - selects the ticket quantity (numberOfTickets)
// - filters for standard tickets only (no platinum or resale)
// - switches to Best Available (instead of Lowest Price)
// - selects the first tickets in the list
// - tries again if there's a popup saying someone else bought the tickets already
// TODO: All these listeners were added haphazardly in testing, I doubt the sequence of actions is as expected
// (e.g. select filters then select sort order then select quantity then buy).
// TODO: Kristine might be interested in "soundcheck" packages - we can also check that by calling clickTicketTypeCheckbox
// example of soundcheck packages: https://www1.ticketmaster.com/the-chainsmokers5-seconds-of-summerlennon-stella-world-war-joy-tour/event/02005646C6716EBF
//Click ticket type icon
sendToSlack("Clicking ticket type")
ClickElement('//*[@id="filter-bar-ticket"]/div[1]/div')
waitForElement(".false, .unbutton .filter-bar__checkbox-toggle-clear", function () {
//Clear all filters
sendToSlack("Clearing filters")
ClickElement('//*[@id="filter-bar-ticket"]/div[2]/div/div[2]/div/div/span[3]/button')
waitForElement("div#quickpicks-module:contains('Sorry')", function() {
//Select Standard tickets only
sendToSlack("Selecting standard tickets only")
clickTicketTypeCheckbox("Standard")
waitForElement('.quick-picks__list-item', function() {
//Sort by Best Available (instead of Lowest Price).
sendToSlack("Sorting by best available")
ClickElement('//*[@id="quickpicks-module"]/div[1]/div/span[3]')
waitForElement('.quick-picks__list-item', function() {
// Select first ticket offering matching filters
sendToSlack("Selecting first available")
ClickElement('(//ul/li[@class = "quick-picks__list-item"])[1]/div/div');
//Change ticket quantity (if applicable)
waitForElement('.offer-card', function() {
//Change the number of tickets (if applicable).
//Note: it looks like the increment button is disabled sometimes.
//We might want to try this upfront before applying filters/sorting.
sendToSlack("Changing ticket quantity")
ChangeTicketQuantity();
//Click the button to Buy the tickets (right hand panel)
ClickElement('//button[@id = "offer-card-buy-button"]');
//Sometimes a dialog comes up if someone else beat us to the tickets.
//This dialog gives a recommendation for a new seat selection.
//If this occurs, we choose to accept the new seats.
waitForElement('.button-aux, .modal-dialog__button', function() {
sendToSlack("Someone else beat us to it, clicking buy")
var sectionChangeBuyButton = getElementByXpath('//button[@class = "button-aux modal-dialog__button"]');
sectionChangeBuyButton.click();
});
// Wow this is lame, class order matters.
waitForElement('.modal-dialog__button, .button-aux', function() {
sendToSlack("Someone else beat us to it, clicking buy")
var sectionChangeBuyButton = getElementByXpath('//button[@class = "modal-dialog__button button-aux"]');
sectionChangeBuyButton.click();
});
window.addEventListener("beforeunload", function (e) {
sendToSlack(publicIp + " got tickets! Go buy now!", buyChannel)
return null;
});
});
})
})
})
})
}
function ChangeTicketQuantity()
{
var rightPanelCurrentTicketCountElement = getElementByXpath('//div[@class = "qty-picker__number qty-picker__number--lg"]');
var currentTicketCount = rightPanelCurrentTicketCountElement.innerText;
var ticketQuantityDifference = numberOfTickets - currentTicketCount;
if (ticketQuantityDifference > 0)
{
var ticketIncrementElement = getElementByXpath('//button[@class = "qty-picker__button qty-picker__button--increment qty-picker__button--sm"]');
for (var i = 0; i < ticketQuantityDifference; i++)
{
try{ticketIncrementElement.click();}catch(ex){}
}
}
else if(ticketQuantityDifference < 0)
{
ticketQuantityDifference = Math.abs(ticketQuantityDifference);
var ticketDecrementElement = getElementByXpath('//button[@class = "qty-picker__button qty-picker__button--decrement qty-picker__button--sm"]');
for (var i = 0; i < ticketQuantityDifference; i++)
{
try{ticketDecrementElement.click();}catch(ex){}
}
}
}
function CheckForGeneralAdmission()
{
var BuyButton = getElementByXpath('//button[@id = "offer-card-buy-button"]');
return BuyButton;
}
function ProcessGeneralAdmission(generalAdmissionBuyButton)
{
ChangeTicketQuantity();
generalAdmissionBuyButton.click();
}
function reload() {
window.top.document.location.replace(window.top.document.location.href);
}
function ClickElement(path, time)
{
var element = getElementByXpath(path);
if(element !== null) {
if (typeof element.click != 'undefined')
{
element.click();
return element;
}
}
}
function getElementByXpath(path)
{
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
var waitForElement = function(selector, callback)
{
if (jQuery(selector).length) {
callback();
} else {
setTimeout(function() {
waitForElement(selector, callback);
}, 100);
}
};
jQuery(document).ready(function() {
var success=false;
//This popup dialog seems to happen in the US ticketmaster website
//We just close it down and continue as normal
SkipPopup();
//Ticket type 1
//This occurs in the majority of ticket sales when there is a selection of ticket types
if(!success)
{
var filterBar = CheckForFilterPanel();
if(filterBar)
{
sendToSlack("These tickets have a filter bar")
getPublicIP(function(ip) {sendToSlack("IP: " + ip)})
console.log('These tickets have a filter bar');
success=true;
ProcessFilterPanel(filterBar);
}
}
//Ticket type 2
//These tickets are General Admission and do not have assigned seating (i.e. no filter bar)
if(!success)
{
var generalAdmissionBuyButton = CheckForGeneralAdmission();
if(generalAdmissionBuyButton)
{
sendToSlack("These tickets are General Admission")
console.log('These tickets are General Admission');
success=true;
ProcessGeneralAdmission(generalAdmissionBuyButton);
}
}
//TODO: Add more ticket types if found
if(!success)
{
sendToSlack("No tickets available yet")
// "h1:contains('Pardon the Interruption')"
// TODO: I don't actually know if we want this. Once we're in the "Waiting room," it sounds like we'll lose our place in line if we refresh. So either we turn this off, or only refresh until we get into the waiting room.
//refresh the page after a random interval between refreshIntervalSecondsMin and refreshIntervalSecondsMax (Tickets weren't yet on sale)
setTimeout(function(){deleteAllCookies();}, refreshIntervalSecondsMin * 1000 + Math.random() * (refreshIntervalSecondsMax - refreshIntervalSecondsMin) * 1000);
}
})