Skip to content

Instantly share code, notes, and snippets.

@Rovack
Last active April 9, 2025 04:19
Show Gist options
  • Save Rovack/51e0fb558ee0fa4ce0e2cd5f0ab17cb1 to your computer and use it in GitHub Desktop.
Save Rovack/51e0fb558ee0fa4ce0e2cd5f0ab17cb1 to your computer and use it in GitHub Desktop.
A simple script that waits for tickets to become available for the Harry Potter Studio Tour in London, and grabs them
// Note that this has some limitations, such as looking specifically for adult tickets,
// looking for the given days only in the nearest month that has availability,
// and always choosing the earliest time if several are found within the desired dates.
function setAdultTickets(adultTicketsWanted) {
const adultTicketsCount = parseInt($('.quantity-control.row > input')[0].value, 10);
const ticketChangeIterations = Math.abs(adultTicketsWanted - adultTicketsCount);
const ticketChangeButton = $(`.quantity-control.row > button.typcn-${adultTicketsCount < adultTicketsWanted ? 'plus' : 'minus'}`)[0];
for (let i = 0; i < ticketChangeIterations; i++) {
ticketChangeButton.click();
}
}
function playSound(src) {
return new Promise((resolve) => {
const audio = new Audio(src);
audio.onended = resolve;
audio.play();
});
}
function repeatHeyListen() {
playSound('https://www.myinstants.com/media/sounds/hey_listen.mp3')
.then(repeatHeyListen);
}
function waitForAvailability() {
return new Promise((resolve) => {
setTimeout(() => {
const availableEls = $('.calendar>.row:not(.blankLoader) .calendar-body .day.available'), isLoading = $('.calendar-modal[data-component=eventTimeModal] .modal-content > .loading-mask.hide').length === 0;
if (isLoading) {
return waitForAvailability()
.then((res) => resolve(res));
}
resolve(availableEls);
}, 1000);
});
}
function playSounds() {
playSound('https://www.myinstants.com/media/sounds/mlg-airhorn.mp3')
.then(() => playSound('https://www.myinstants.com/media/sounds/sound-9______.mp3'))
.then(() => playSound('https://www.myinstants.com/media/sounds/ps_1.mp3'))
.then(() => playSound('https://www.myinstants.com/media/sounds/wrong-answer-sound-effect.mp3'))
.then(() => playSound('https://www.myinstants.com/media/sounds/lalalalala.swf.mp3'))
.then(() => playSound('https://www.myinstants.com/media/sounds/tuturu_1.mp3'))
.then(() => playSound('https://www.myinstants.com/media/sounds/hallelujahshort.swf.mp3'))
.then(repeatHeyListen);
}
function addTicketsToBasket(dayElement) {
dayElement.click();
setTimeout(() => waitForAvailability()
.then(() => {
$('.ui-control.button.select-time')[0].click();
setTimeout(() => {
$('.typcn.typcn-shopping-cart.ng-binding')[0].click();
}, 2000);
}), 2000);
}
function checkForTickets(datesWanted=[6, 7, 8], adultTicketsWanted=2, checkFrequency=15) {
setAdultTickets(adultTicketsWanted);
function check() {
$('.shared-calendar-button').click();
waitForAvailability()
.then(availableEls => {
console.log(new Date(), 'Availability loaded. Checking for relevant dates...');
for (let i = 0; i < availableEls.length; i++) {
const day = parseInt(availableEls[i].innerText, 10);
console.log('Day', day, 'is available...');
if (datesWanted.includes(day)) {
console.log('Found tickets!!!!!');
playSounds();
addTicketsToBasket(availableEls[i]);
return;
}
}
console.log(`Relevant dates not yet available. Will check again in ${checkFrequency} seconds.`);
$('#page > div:nth-child(11) > div.modal.info-modal.w-auto-c > div > div.close').click();
setTimeout(check, checkFrequency * 1000);
});
};
check();
}
@Rovack
Copy link
Author

Rovack commented Feb 25, 2025

Awesome, glad you got it running @wired14! :) Definitely let me know if you run into any problems.

@wired14
Copy link

wired14 commented Mar 17, 2025

Awesome, glad you got it running @wired14! :) Definitely let me know if you run into any problems.

Was finally able to get tickets today. @Rovack. I had to use the modified version. It would time out after a day or so and I would have to re-run. I was able to get tickets this morning when I logged in to refresh the script and it immediately went off. I had to run two scripts to catch days in April and May.

Additionally, there is a queue system used occasionally, but only to initially enter the site. Also, not sure how the hourRange was used, but worked as is.

Example usage:

function setAdultTickets(adultTicketsWanted) {
	const adultTicketsCount = parseInt($('.quantity-control.row > input')[0].value, 4);
	const ticketChangeIterations = Math.abs(adultTicketsWanted - adultTicketsCount);
	const ticketChangeButton = $(`.quantity-control.row > button.typcn-${adultTicketsCount < adultTicketsWanted ? 'plus' : 'minus'}`)[0];

	for (let i = 0; i < ticketChangeIterations; i++) {
		ticketChangeButton.click();
	}
}

function playSound(src) {
	return new Promise((resolve) => {
		const audio = new Audio(src);
		audio.onended = resolve;
		audio.play();
	});
}

function repeatHeyListen() {
	playSound('https://www.myinstants.com/media/sounds/hey_listen.mp3')
		.then(repeatHeyListen);
}

function waitForAvailability(monthWanted) {
	return new Promise((resolve) => {
		setTimeout(() => {
			const availableEls = $('.calendar>.row:not(.blankLoader) .calendar-body .day.available'), isLoading = $('.calendar-modal[data-component=eventTimeModal] .modal-content > .loading-mask.hide').length === 0;
			if (isLoading) {
				return waitForAvailability(monthWanted)
					.then((res) => resolve(res));
			}

			if (monthWanted == null) {
				resolve({ availableEls });
			}

			const monthValue = $('[name="ctl00$ContentPlaceHolder$SalesChannelDetailControl$EventsDateTimeSelectorModal$EventsDateTimeSelector$CalendarSelector$MonthDropDownList"]')[0].value;
			const month = parseInt(monthValue.replace(/^\D+/g, ''), 10);

			if (month < monthWanted) {
				console.log(`Month too early (${month}) - skipping to next month.`);
				$('[name="ctl00$ContentPlaceHolder$SalesChannelDetailControl$EventsDateTimeSelectorModal$EventsDateTimeSelector$CalendarSelector$NextMonthImageButton"]').click();
				return waitForAvailability(monthWanted).then(res => resolve(res));
			}

			resolve({ availableEls, month });
		}, 1000);
	});
}

function playSounds() {
	playSound('https://www.myinstants.com/media/sounds/mlg-airhorn.mp3')
		.then(() => playSound('https://www.myinstants.com/media/sounds/sound-9______.mp3'))
		.then(() => playSound('https://www.myinstants.com/media/sounds/ps_1.mp3'))
		.then(() => playSound('https://www.myinstants.com/media/sounds/wrong-answer-sound-effect.mp3'))
		.then(() => playSound('https://www.myinstants.com/media/sounds/lalalalala.swf.mp3'))
		.then(() => playSound('https://www.myinstants.com/media/sounds/tuturu_1.mp3'))
		.then(() => playSound('https://www.myinstants.com/media/sounds/hallelujahshort.swf.mp3'))
		.then(repeatHeyListen);
}

async function addTicketsToBasket(dayElement) {
	dayElement.click();

	await new Promise((resolve) => setTimeout(resolve, 2000));
	await waitForAvailability();

	const chooseTimeButton = $('.ui-control.button.select-time:not(.disabled)')[0];
	if (!chooseTimeButton) return false;

	console.log('Found tickets!!!!!');
	playSounds();

	chooseTimeButton.click();

	await new Promise((resolve) => setTimeout(resolve, 2000));

	$('.typcn.typcn-shopping-cart.ng-binding')[0].click();
	return true;
}

function checkForTickets(datesWanted=[4,5,6], adultTicketsWanted=4, checkFrequency=15, monthWanted=5, hourRange=9-8) {
	setAdultTickets(adultTicketsWanted);

	async function check() {
		try {
			if ($('.ui-control.button.extendSession').length != 0) {
				console.log('Extending session');
				$('.ui-control.button.extendSession').click();
			}
			
			$('.shared-calendar-button').click();

			await waitForAvailability(monthWanted)
				.then(async ({ availableEls, month }) => {
					console.log(new Date(), `Availability loaded${month != null ? ` for month ${month}` : ''}. Checking for relevant dates...`);

					if (monthWanted != null && month > monthWanted) {
						console.log(`Month is too late (${month}). Will check again in ${checkFrequency} seconds.`);
						setTimeout(check, checkFrequency * 1000);
						return;
					}

					for (let i = 0; i < availableEls.length; i++) {
						const day = parseInt($('.calendar>.row:not(.blankLoader) .calendar-body .day.available')[i].innerText, 10);
						console.log('Day', day, 'is available...');
						if (datesWanted.includes(day)) {
							const succeeded = await addTicketsToBasket($('.calendar>.row:not(.blankLoader) .calendar-body .day.available')[i], hourRange);
							if (succeeded) return;
						}
					}

					console.log(`Relevant dates not yet available. Will check again in ${checkFrequency} seconds.`);
					setTimeout(check, checkFrequency * 1000);
			});
		} catch (err) {
			console.error('Error checking. Just gonna keep trying.', err);
			setTimeout(check, checkFrequency * 1000);
		}
	};
	check();
}

function checkForTicketsInMonth(datesWanted, monthWanted, adultTicketsWanted, checkFrequency) {
	return checkForTickets(datesWanted, adultTicketsWanted, checkFrequency, monthWanted);
}

@AngshumanDey
Copy link

Any guide how can run this code to monitor the changes in ticket status and notify me?
Can I run it on phone?
Or do I need to host a webpage?

@wired14
Copy link

wired14 commented Mar 18, 2025

Any guide how can run this code to monitor the changes in ticket status and notify me? Can I run it on phone? Or do I need to host a webpage?

Open console on your browser that is under developer tools. Update fields in checkForTickets for dates you want. Paste code snippet in console. Then run “checkForTickets()”.

@Rovack
Copy link
Author

Rovack commented Mar 18, 2025

Yay, thanks for the update @wired14! Hope you have a great time. :)

And hi, @AngshumanDey.

Any guide how can run this code to monitor the changes in ticket status and notify me? Can I run it on phone? Or do I need to host a webpage?

I don't believe you can easily run it on a phone, but you don't need to host a webpage or anything either. On a computer, all you have to do is open the browser's Console, and there you can paste the code and run it.

You can see the basic instructions in the first comment here, but you might also want to scan through the comments posted since then, which may contain updates and solutions for different problems folks have run into.

@AngshumanDey
Copy link

@wired14 Thanks. It works now

@Rovack : The script is great. It worked for the test dates. My problem is now I have to keep my computer running until my desired dates are available. Not sure how long that is going to take sadly.

@wired14
Copy link

wired14 commented Mar 18, 2025

my

Great to hear. Looking at the dates that become available, a few tickets are more likely a month ahead of time, especially the couple days before. It took me 3 weeks of checking to get it. Just had to check in on the script once a day and make sure it was still running.

@AngshumanDey
Copy link

my

Great to hear. Looking at the dates that become available, a few tickets are more likely a month ahead of time, especially the couple days before. It took me 3 weeks of checking to get it. Just had to check in on the script once a day and make sure it was still running.

What do you mean when you say check it on the script once a day? I suppose you have a PC that is running 24x7 without going to stanby, right? I am not sure how to do that when I am at work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment