Last active
March 30, 2021 14:39
-
-
Save rubencodes/a87351e4e14d034917adf5cdc7648080 to your computer and use it in GitHub Desktop.
Check for appointments on the NY State vaccination website. https://am-i-eligible.covid19vaccine.health.ny.gov/Public/providers
This file contains 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
/* | |
* This script will automatically play a sound and trigger an alert when it finds available appointments | |
* on the New York State vaccine finder website. https://am-i-eligible.covid19vaccine.health.ny.gov/Public/providers | |
* It can be run by pasting it into the Chrome console at the above URL. | |
* | |
* NOTE: You must meet certain eligibility criteria to qualify for a vaccine. | |
*/ | |
// Map of all providers - use to populate `providersToCheck` field. | |
const Providers = { | |
// Open to all eligible. | |
SUNYAlbany: 1003, | |
SUNYBinghamton: 1009, | |
SUNYCorningCommunityCollege: 1025, | |
SUNYOldWestBury: 1029, | |
SUNYOneonta: 1030, | |
SUNYOrange: 1031, | |
SUNYPolytechnicInstitute: 1010, | |
SUNYPotsdam: 1006, | |
SUNYRocklandCommunityCollege: 1035, | |
SUNYStonyBrook: 1005, | |
AqueductRacetrack: 1007, | |
BronxBayEdenSeniorCenter: 1024, | |
ConferenceCenterNiagaraFalls: 1026, | |
JavitsCenter: [1000, 1019], | |
JonesBeachField3: 1001, | |
PlattsburghInternationalAirport: 1008, | |
QueensburyAviationMallSears: [1027, 1034], | |
RochesterDomeArena: 1012, | |
StateFairExpoCenter: [1002, 1020], | |
StonyBrookSouthampton: 1032, | |
SuffolkCCCBrentwood: 1028, | |
UlsterFairgroundsInNewPaltz: 1033, | |
UniversityAtBuffaloSouthCampus: 1011, | |
WestchesterCountyCenter: 1004, | |
// Restricted by location! When filtering by these, you have to | |
// enter the "address" field in the payload, otherwise it doesn't | |
// know it should offer you these options. | |
RESTRICTED_AlbanySchenectadyTroy_WashingtonAvenueArmory: 1016, | |
RESTRICTED_Brooklyn_MedgarEversCollege: 1014, | |
RESTRICTED_Buffalo_DelavanGriderCommunityCenter: 1018, | |
RESTRICTED_Queens_YorkCollegeHealthAndPhysicalEducationComplex: 1013, | |
RESTRICTED_Rochester_FormerKodakHawkeyeParklingLot: 1017, | |
RESTRICTED_YonkerMountVernon_NewYorkNationalGuardArmory: 1015, | |
}; | |
const APPOINTMENT_STATUS = { | |
NotAvailable: "NAC", // None Available Currently | |
Available: "AA", // Appointments Available | |
}; | |
/* | |
* Plays a sound and fires an alert with the provided text. | |
* @param {string} text - the text to show in the alert. | |
* @param {string} [audioFile] - the path to the audio file to play. Defaults to an annoying "beep" sound. | |
*/ | |
function alertWithSound( | |
text, | |
audioFile = "https://media.geeksforgeeks.org/wp-content/uploads/20190531135120/beep.mp3", // Found this online. | |
) { | |
const alertSound = new Audio(audioFile); | |
// Alert when the sound ends (so that the alert doesn't block the sound). | |
alertSound.addEventListener("ended", () => alert(text)); | |
alertSound.play(); | |
} | |
/* | |
* Recursively flattens the provided array. | |
* e.g. [1, [2, [3]], 4] => [1, 2, 3, 4] | |
* @param {array} arr - the array to flatten. | |
* @returns {array} [flattenedArr] - the fully flattened array. | |
*/ | |
function flatten(arr) { | |
return arr.reduce((acc, curr) => [ | |
...acc, | |
...(Array.isArray(curr) ? flatten(curr) : [curr]) | |
], []); | |
} | |
/* | |
* Returns the current date-time as a formatted string. | |
* @returns {string} | |
*/ | |
function getCurrentDateTime() { | |
const currentDate = new Date(); | |
const dateTimeString = `${currentDate.toDateString()} ${currentDate.toTimeString().slice(0, 8)}`; | |
return dateTimeString; | |
} | |
/* | |
* Makes a network request to the provider search API, and returns JSON results. | |
* @param {object} payload - the JSON payload to pass the API. | |
* @returns {Promise} | |
*/ | |
async function findLocationsWithAppts(payload) { | |
return (await fetch("https://am-i-eligible.covid19vaccine.health.ny.gov/api/get-providers", { | |
"headers": { | |
"accept": "application/json, text/plain, */*", | |
"accept-language": "en-US,en;q=0.9", | |
"content-type": "application/json;charset=UTF-8", | |
"sec-ch-ua": "\"Chromium\";v=\"88\", \"Google Chrome\";v=\"88\", \";Not A Brand\";v=\"99\"", | |
"sec-ch-ua-mobile": "?0", | |
"sec-fetch-dest": "empty", | |
"sec-fetch-mode": "cors", | |
"sec-fetch-site": "same-origin", | |
"token": "disabled" | |
}, | |
"referrer": "https://am-i-eligible.covid19vaccine.health.ny.gov/Public/providers", | |
"referrerPolicy": "strict-origin-when-cross-origin", | |
"body": JSON.stringify(payload), | |
"method": "POST", | |
"mode": "cors", | |
"credentials": "include" | |
})).json(); | |
} | |
/* | |
* Alerts when available appointments are found matching the given parameters. | |
* @param {object} payload - the JSON payload to pass the API. | |
* @param {object} [options] - configuration options for the alert behavior. | |
*/ | |
async function alertOnFoundAppointments(payload = {}, options = {}) { | |
const { interval, playSound } = options; | |
const providersToCheck = options.providersToCheck ? flatten(options.providersToCheck) : null; | |
const allLocations = await findLocationsWithAppts(payload); | |
const approvedLocations = allLocations | |
.filter(({ providerId }) => !providersToCheck || providersToCheck.includes(providerId)); | |
const approvedLocationsWithAppointments = approvedLocations | |
.filter(({ availableAppointments }) => availableAppointments === APPOINTMENT_STATUS.Available); | |
// Alert out success. | |
const [locationWithAppts] = approvedLocationsWithAppointments; | |
if (locationWithAppts) { | |
const numLocationsWithAppts = approvedLocationsWithAppointments.length; | |
const successText = numLocationsWithAppts > 1 | |
? `${locationWithAppts.providerName} and ${numLocationsWithAppts - 1} others have appointments!` | |
: `${locationWithAppts.providerName} has appointments!`; | |
const dateTimeString = getCurrentDateTime(); | |
console.log(`${dateTimeString}: ${successText}`); | |
playSound ? alertWithSound(successText) : alert(successText); | |
return; | |
} else { | |
// Print out failure. | |
const dateTimeString = getCurrentDateTime(); | |
const failureText = `${dateTimeString}: No appointments found at ${approvedLocations.length} of ${allLocations.length} locations.`; | |
console.log(failureText); | |
} | |
// Check again. | |
if (interval) { | |
setTimeout( | |
() => alertOnFoundAppointments(payload, options), | |
interval | |
); | |
} | |
} | |
// ApplicationId was retrieved by sniffing an API call, but | |
// seems to be optional along with the rest of this data. | |
// UNLESS you are filtering by a restricted location, in which | |
// case you need to enter an eligible ZIP code for the address. | |
const payload = { | |
applicationId: "your-application-id", | |
address: "your-zip-code", | |
dob: "mm/dd/yyyy", | |
miles: "100", | |
}; | |
// Configuration options. | |
const options = { | |
// How often to check, in MS. | |
// Defaults to not rechecking. | |
interval: 10 * 1000, // 10s | |
// Array of provider ids we care about checking. | |
// Defaults to check all when falsy. | |
providersToCheck: null, // or, e.g. [Providers.JavitsCenter] | |
// Whether to play an annoying sound when an appointment is found. | |
// Defaults to false. | |
playSound: true, | |
}; | |
// Start checking. | |
alertOnFoundAppointments(payload, options); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment