-
-
Save allyellujah/14b998233c5eb85596dbb1e747675cfe to your computer and use it in GitHub Desktop.
Automatically book 2nd dose vaccine at closest location Ontario
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** | |
* NOTICE: I have been told that the script no longer works and has been blocked. | |
* | |
* It is unfortunate that the Ministry and Accenture don't see | |
* this as an opportunity to improve the experience for their users. | |
* | |
* I was able to create a better user experience using nothing more than | |
* their own website's code and their own API. When 100 people want the same | |
* 1 appointment, and they have to race against each other to click first, and 99% | |
* of those people will not get the appointment, 99% of people will be disappointed. | |
* That might be acceptable for trying to buy rare sneakers, but not for | |
* vaccination and medical care in general. | |
* | |
* Implementing this kind of "match me with my preferences" on the server-side | |
* would allow people to simply 'queue up' while the system tries to match them | |
* with an appointment automatically, and it's clearly already possible with the | |
* infrastructure that is in place. | |
* | |
* If you used the script and it has helped you, consider this experience next | |
* time you think your government or your employer should cut costs on software | |
* and IT. | |
* | |
* I am very thankful for everyone who has messaged me their support. The original | |
* script remains below. | |
*/ | |
throw "It doesn't work anymore."; | |
/*** | |
* Automated Vaccine Booker Thing | |
* | |
* I wish everything had an API. | |
* | |
* ____________________ | |
* / PLEASE \ | |
* ! READ ! | |
* ! ENTIRELY ! | |
* \____________________/ | |
* ! ! | |
* ! ! | |
* L_ ! | |
* / _)! | |
* / /__L | |
* _____/ (____) | |
* (____) | |
* _____ (____) | |
* \_(____) | |
* ! ! | |
* ! ! | |
* \__/ | |
* | |
* NOTE - THIS WILL AUTOMATICALLY BOOK WITH NO CONFIRMATION. | |
* It will replace your existing 2nd dose appointment | |
* if you have one. | |
* | |
* 1. You must log in through https://covid19.ontariohealth.ca/booking-home | |
* and be eligible for 2nd dose. | |
* 2. When you select the option that isn't Pharmacy, you are sent to | |
* https://vaccine.covaxonbooking.ca/location-search | |
* You MUST be on this page as pictured: https://i.imgur.com/dfAe1sD.png | |
* 3. Fill in your information below. Your location will be used for the distance | |
* calculation. Your personal info will be used to book the appointment. | |
* 4. Paste the entire file into the browser's Web Developer Tools console. | |
* 5. Output will be in the console. Your confirmation will be in your email on success. | |
*/ | |
// YOUR INFORMATION GOES HERE: | |
// *************************** | |
let FIRSTNAME = "FNAME"; | |
let LASTNAME = "LNAME"; | |
let EMAIL = "[email protected]"; | |
let PHONE = "+14165551234"; // <- in that format | |
let YOUR_LOCATION = {lat: 43.646723, lng: -79.413695}; // use Google Maps | |
let MAX_DISTANCE_METERS = 10000; | |
let MAX_DATE = "2021-06-27"; | |
// *************************** | |
let cancelToken = { cancelled: false }; | |
let cancel = () => cancelToken.cancelled = true; | |
let go = async (c) => { | |
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | |
const API = parcelRequire("bpkL").ServicesContext._currentValue.bookingService; | |
API._apiCommService._baseURL = "https://api.covaxonbooking.ca/public"; | |
const log = (...o) => console.log(new Date().toLocaleString(), ...o); | |
let session = {}; | |
try { | |
session = Object.entries(document.getElementById('root'))[3][1]._internalRoot | |
.current.lastEffect.memoizedState.next.next.memoizedState; | |
} catch { | |
throw "Error: You MUST be on the 'Find a Vaccination Center Location - " + | |
"Enter a City, Address or Postal Code' page before running this. " + | |
"Please refresh and go to that screen first."; | |
} | |
var successfullyBooked = false; | |
while (!successfullyBooked) { | |
const find_good_location = async () => { | |
const result = await API.searchLocations({ | |
focusPoint: YOUR_LOCATION, | |
fromDate: new Date, | |
vaccineData: parcelRequire("8vBb").getVaccineDataAtStepOrLatest(session.vaccineData, ["screening"]), | |
locationQuery: session.locationQuery || void 0, | |
doseNumber: session.appointmentRescheduleFrom || session.previousAppointments.length + 1, | |
includeThirdPartyLocations: true, | |
cursor: "", | |
limit: 20, | |
locationType: "CombinedBooking" | |
}); | |
log("looking..."); | |
const vloc = result.locations.find( l => l.distanceInMeters <= MAX_DISTANCE_METERS); | |
if (vloc === undefined && result.locations[0] !== undefined) { | |
log("closest location found is too far", result.locations[0]); | |
} else { | |
log("no locations found", result); | |
} | |
return vloc !== undefined && vloc; | |
}; | |
var vaxLocation = false; | |
while (vaxLocation === false) { | |
if (c.cancelled) throw "cancelled"; | |
await sleep(500); | |
vaxLocation = await find_good_location(); | |
} | |
log("got a location", vaxLocation); | |
const startDate = new Date(); | |
const endDate = new Date(MAX_DATE); | |
const vaccineData = session.vaccineData.latest; | |
const doseNumber = 2; | |
const availabilityResponse = await API.getLocationAvailability(vaxLocation, startDate, endDate, vaccineData, doseNumber); | |
const availableDate = availabilityResponse.availability.find( a => a.available ); | |
if (!availableDate) { | |
log("no availability...", availabilityResponse); | |
await sleep(500); | |
continue; | |
} | |
log("got a date with availability", availableDate); | |
const slotsResponse = await API.getLocationSlots(vaxLocation, availableDate.date, vaccineData); | |
const availableSlot = slotsResponse.slotsWithAvailability.find( _ => true ); | |
if (availableSlot === undefined) { | |
log("unable to find an available slot", slotsResponse); | |
continue; | |
} | |
log("found a time slot", availableSlot); | |
const reserveSlotResponse = await API.reserveSlot(doseNumber, vaxLocation, | |
availableDate.date, availableSlot.localStartTime, | |
vaccineData, "", session.sessionId); | |
if (!reserveSlotResponse.value || !reserveSlotResponse.value.reservationId) { | |
log("unable to reserve the timeslot", reserveSlotResponse); | |
continue; | |
} | |
log("got a reserved slot", reserveSlotResponse); | |
const reservationId = reserveSlotResponse.value.reservationId; | |
const personalDetails = [ | |
{ "id": "q.patient.firstname", "value": FIRSTNAME, "type": "text" }, | |
{ "id": "q.patient.lastname", "value": LASTNAME, "type": "text" }, | |
{ "id": "q.patient.email", "value": EMAIL, "type": "email" }, | |
{ "id": "q.patient.mobile", "value": PHONE, "type": "mobile-phone" }]; | |
const additionalQuestions = [ | |
{"id": "q.patient.proxy.section.hideshow","value": "No","type": "single-select"}, | |
{"id": "q.patient.desc.proxy.name","type": "text"}, | |
{"id": "q.patient.desc.proxy.phone","type": "text"}, | |
{"id": "q.patient.desc.relationship.to.the.client","type": "single-select"}]; | |
const createAppointmentResponse = await API.createAppointment({ | |
eligibilityQuestions: [], | |
personalDetails, | |
additionalQuestions, | |
appointments: [ { reservationId: reservationId } ], | |
oneTime: "", | |
locale: "en-CA", | |
externalAppointments: session.externalAppointments | |
}); | |
log("Success! Check your email.", createAppointmentResponse); | |
successfullyBooked = true; | |
} | |
} | |
try { | |
await go(cancelToken); | |
} catch (error) { | |
console.error("an unexpected error occurred. try logging in again " + | |
"if the following error doesn't mean anything:\n", error); | |
} | |
// Type cancel(); in the console to stop. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment