Skip to content

Instantly share code, notes, and snippets.

@jonathan-reisdorf
Last active June 24, 2023 15:10
Show Gist options
  • Save jonathan-reisdorf/e3d92add55750117cc02b6b9854dafcd to your computer and use it in GitHub Desktop.
Save jonathan-reisdorf/e3d92add55750117cc02b6b9854dafcd to your computer and use it in GitHub Desktop.
add "taken" page
// ==UserScript==
// @name Bürgeramt Termin Hunting
// @description Helfer fürs Bürgeramt
// @include htt*://service.berlin.de/terminvereinbarung*
// @version 2.02
// ==/UserScript==
const MIN_RELOAD_DELAY = 60000;
const ADDITIONAL_RELOAD_DELAY = Math.round(Math.random() * 30000);
const HUNTING_CONFIG = {
acceptableDayRange: 99,
excludeSameDay: true,
isTestMode: false,
name:
localStorage.getItem('buergeramt_name') ||
localStorage.setItem('buergeramt_name', prompt('Vor- und Nachname eintragen') || ''),
email:
localStorage.getItem('buergeramt_email') ||
localStorage.setItem('buergeramt_email', prompt('E-Mail-Adresse eintragen') || ''),
phone:
localStorage.getItem('buergeramt_phone') ||
localStorage.setItem('buergeramt_phone', prompt('Telefonnummer eintragen') || ''),
};
class BuergeramtTerminHunting {
constructor() {
window.CheckInput = () => true; // bypass form check
if (!this.areNotificationsAllowed && !this.areNotificationsRejected) {
Notification.requestPermission();
}
document.readyState === 'complete' && this.start();
document.onreadystatechange = () => document.readyState === 'complete' && this.start();
}
get currentPage() {
const { href } = document.location;
const pageMap = {
taken: href.includes('terminvereinbarung/termin/taken'),
calendar: href.includes('terminvereinbarung/termin/day'),
captcha: href.includes('terminvereinbarung/termin/human'),
appointment: href.includes('terminvereinbarung/termin/time'),
book: href.includes('terminvereinbarung/termin/register'),
};
return Object.keys(pageMap).find((key) => pageMap[key]) ?? 'unknown';
}
get areNotificationsAllowed() {
return Notification.permission === 'granted';
}
get areNotificationsRejected() {
return Notification.permission === 'denied';
}
start() {
const { currentPage } = this;
currentPage === 'taken' && this.handleTakenPage();
currentPage === 'calendar' && this.handleCalendarPage();
currentPage === 'captcha' && this.handleCaptchaPage();
currentPage === 'appointment' && this.handleAppointmentPage();
currentPage === 'book' && this.handleBookPage();
currentPage === 'unknown' && this.handleUnknownPage();
}
notify(text, halt = false) {
this.areNotificationsAllowed && new Notification(text);
halt && alert(text);
}
reloadPage() {
setTimeout(() => document.location.reload(), MIN_RELOAD_DELAY + ADDITIONAL_RELOAD_DELAY);
}
handleTakenPage() {
setInterval(() => document.querySelector('form[action*="termin/restart"] button:not([disabled])')?.click(), 1000);
}
handleCalendarPage() {
const SELECTOR_NON_BOOKABLE = '.calendar-month-table td.nichtbuchbar';
const SELECTOR_BOOKABLE = '.calendar-month-table td.buchbar';
const CLASSNAME_TODAY = 'heutemarkierung';
const allNodes = [...document.querySelectorAll(`${SELECTOR_BOOKABLE}, ${SELECTOR_NON_BOOKABLE}`)];
const bookableNode = [...document.querySelectorAll(SELECTOR_BOOKABLE)].find((node) => {
const index = allNodes.indexOf(node);
const isToday = [...node.classList].includes(CLASSNAME_TODAY);
return index + 1 < HUNTING_CONFIG.acceptableDayRange && (!HUNTING_CONFIG.excludeSameDay || !isToday);
});
if (bookableNode) {
this.notify('[Bürgeramt] Termin gefunden!');
bookableNode.querySelector('a').click();
} else {
this.reloadPage();
}
}
handleCaptchaPage() {
this.notify('[Bürgeramt] Captcha lösen!', true);
}
handleAppointmentPage() {
const SELECTOR_BOOKABLE = '.calendar-table a[href^="/terminvereinbarung/termin/time/"]';
const bookableNodeLink = document.querySelector(SELECTOR_BOOKABLE);
if (!bookableNodeLink) {
document.location.href = 'https://service.berlin.de/terminvereinbarung/termin/day/';
return;
}
bookableNodeLink.click();
}
handleBookPage() {
this.notify('[Bürgeramt] Terminbuchung wird abgeschlossen!');
const formNameEl = document.querySelector('input[name=familyName]');
const formEmailEl = document.querySelector('input[name=email]');
const formPhoneEl = document.querySelector('input[name=telephone]');
const formSurveyAcceptedEl = document.querySelector('select[name=surveyAccepted]');
const formAcceptTermsEl = document.querySelector('input[name=agbgelesen]');
const formSubmitEl = document.getElementById('register_submit');
formNameEl && (formNameEl.value = HUNTING_CONFIG.name);
formEmailEl && (formEmailEl.value = HUNTING_CONFIG.email);
formPhoneEl && (formPhoneEl.value = HUNTING_CONFIG.phone);
formSurveyAcceptedEl && (formSurveyAcceptedEl.value = '0');
formAcceptTermsEl && (formAcceptTermsEl.checked = true);
!HUNTING_CONFIG.isTestMode && formSubmitEl && formSubmitEl.click();
}
handleUnknownPage() {
this.reloadPage();
}
}
new BuergeramtTerminHunting();
@jonathan-reisdorf
Copy link
Author

jonathan-reisdorf commented Apr 6, 2022

Tested with Chrome + Tampermonkey (should work with FF + Greasemonkey, too)

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