Skip to content

Instantly share code, notes, and snippets.

@rubencodes
Last active March 30, 2021 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rubencodes/a87351e4e14d034917adf5cdc7648080 to your computer and use it in GitHub Desktop.
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 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