Skip to content

Instantly share code, notes, and snippets.

@martin-v
Forked from lopis/berlin-terminator.user.js
Last active April 9, 2024 15:58
Show Gist options
  • Save martin-v/1bb9fb51b97dc9ebc82d94e70b4f472d to your computer and use it in GitHub Desktop.
Save martin-v/1bb9fb51b97dc9ebc82d94e70b4f472d to your computer and use it in GitHub Desktop.
Script that automates hunting for a new Termin in Berlin. Requires a browser extension that can run user scripts., e.g. https://www.tampermonkey.net/
// ==UserScript==
// @name Terminator
// @description Automates finding appointments in Berlin Buergeraemter
// @namespace https://service.berlin.de/terminvereinbarung/termin
// @match https://service.berlin.de/terminvereinbarung/termin/*
// @version 5
// @grant none
// ==/UserScript==
'use strict'
/* Set your personal details here */
var name = 'Your Full Name';
var email = 'youtemail@mail.com';
var phoneNumber = '+491000000000';
var submit = false;
// If `true`, it will book an appointment for you,
// If `false`, it will reserve an appointment for a few minutes and fill in the form, but it will not book the appointment (it will beep to let you know).
/* define your desire and locations below the lists */
/*******************************/
// List of all Buergeraemter with ID
// extracted here https://service.berlin.de/standorte/buergeraemter/
// Bezirksamt_Charlottenburg_Wilmersdorf
var Buergeramt_Halemweg_Aussenstelle = 122210;
var Buergeramt_Heerstrasse = 122217;
var Buergeramt_Heerstrasse_Vorzugstermine = 327316;
var Buergeramt_Hohenzollerndamm_Fluechtlingsbuergeramt = 122219;
var Buergeramt_Hohenzollerndamm_Vorzugstermine = 327312;
var Buergeramt_Wilmersdorfer_Strasse = 122227;
var Buergeramt_Wilmersdorfer_Strasse_Vorzugstermine = 327314;
// Bezirksamt_Friedrichshain_Kreuzberg
var Buergeramt_1_Kreuzberg_Yorckstrasse = 122231;
var Buergeramt_1_Kreuzberg_Yorckstrasse_Abholung_von_Dokumenten = 329673;
var Buergeramt_1_Kreuzberg_Yorckstrasse_Vorzugstermine = 327346;
var Buergeramt_2_0_Ausbildungsbuergeramt_Schlesische_Str = 122238;
var Buergeramt_3_Friedrichshain_Frankfurter_Allee = 122243;
var Buergeramt_3_Friedrichshain_Frankfurter_Allee_Abholung_von_Dokumenten = 329667;
var Buergeramt_3_Friedrichshain_Frankfurter_Allee_Vorzugstermine = 327348;
// Bezirksamt_Lichtenberg
var Ausbildungsbuergeramt_Alt_Hohenschoenhausen = 122254;
var Ausbildungsbuergeramt_Alt_Hohenschoenhausen_Abholung_von_Dokumenten = 329586;
var Ausbildungsbuergeramt_Alt_Hohenschoenhausen_Sondertermine_2 = 331011;
var Ausbildungsbuergeramt_Alt_Hohenschoenhausen_Sondertermine_1 = 349977;
var Buergeramt_1_Neu_Hohenschoenhausen = 122252;
var Buergeramt_1_Neu_Hohenschoenhausen_Abholung_von_Dokumenten = 329577;
var Buergeramt_1_Neu_Hohenschoenhausen_Vorzugstermine = 329742;
var Buergeramt_2_Lichtenberg = 122260;
var Buergeramt_2_Lichtenberg_Abholung_von_Dokumenten = 329580;
var Buergeramt_2_Lichtenberg_Vorzugstermine = 329745;
var Buergeramt_3_Friedrichsfelde_Tierparkcenter = 122262;
var Buergeramt_3_Friedrichsfelde_Tierparkcenter_Abholung_von_Dokumenten = 329583;
var Buergeramt_3_Friedrichsfelde_Tierparkcenter_Vorzugstermine = 329748;
var Mobiles_Buergeramt = 327761;
// Bezirksamt_Marzahn_Hellersdorf
var Buergeramt_Biesdorf_Center = 122271;
var Buergeramt_Biesdorf_Center_Vorzugstermine = 327278;
var Buergeramt_Helle_Mitte = 122273;
var Buergeramt_Helle_Mitte_Vorzugstermine = 327274;
var Buergeramt_Marzahner_Promenade = 122277;
var Buergeramt_Marzahner_Promenade_Vorzugstermine = 327276;
// Bezirksamt_Mitte (TODO)
var Buergeramt_4_Fluechtlingsbuergeramt_Mitte_Vorzugstermine = 329757;
var Buergeramt_Klosterstrasse = 351065;
var Buergeramt_Klosterstrasse_Abholung_von_Dokumenten = 330439;
var Buergeramt_Klosterstrasse_Vorzugstermine = 330436;
var Buergeramt_Rathaus_Mitte = 122280;
var Buergeramt_Rathaus_Mitte_Abholung_von_Dokumenten = 329613;
var Buergeramt_Rathaus_Mitte_Vorzugstermine = 327294;
var Buergeramt_Rathaus_Tiergarten = 122282;
var Buergeramt_Rathaus_Tiergarten_Abholung_von_Dokumenten = 329607;
var Buergeramt_Rathaus_Tiergarten_Vorzugstermine = 327290;
var Buergeramt_Wedding = 122284;
var Buergeramt_Wedding_Abholung_von_Dokumenten = 329610;
var Buergeramt_Wedding_Strassenumbenennungen_Luederitzstrasse_Nachtigalplatz = 331578;
var Buergeramt_Wedding_Vorzugstermine = 327292;
var Buergeramt_Bereich_Leistungen_fuer_Bildung_und_Teilhabe = 326001;
var Fluechtlingsbuergeramt_Rathaus_Tiergarten = 327539;
var Wohngeldberatung_Mitte = 327066;
// Bezirksamt_Neukoelln
var Buergeramt_Blaschkoallee = 122291;
var Buergeramt_Blaschkoallee_Vorzugstermine = 327270;
var Buergeramt_Rathaus_Neukoelln = 122285;
var Buergeramt_Rathaus_Neukoelln_Vorzugstermine = 327266;
var Buergeramt_Sonnenallee = 122286;
var Buergeramt_Sonnenallee_Vorzugstermine = 327264;
var Buergeramt_Zwickauer_Damm = 122296;
var Buergeramt_Zwickauer_Damm_Vorzugstermine = 327268;
// Bezirksamt_Pankow
var Buergeramt_Karow_Buch = 150230;
var Buergeramt_Karow_Buch_Abholung_von_Dokumenten = 329685;
var Buergeramt_Karow_Buch_Vorzugstermine = 329760;
var Buergeramt_Pankow = 122301;
var Buergeramt_Pankow_Abholung_von_Dokumenten = 329691;
var Buergeramt_Pankow_Vorzugstermine = 327282;
var Buergeramt_Prenzlauer_Berg = 122297;
var Buergeramt_Prenzlauer_Berg_Abholung_von_Dokumenten = 329688;
var Buergeramt_Prenzlauer_Berg_Parkraumbewirtschaftung = 326283;
var Buergeramt_Prenzlauer_Berg_Vorzugstermine = 327286;
var Buergeramt_Weissensee = 122294;
var Buergeramt_Weissensee_Abholung_von_Dokumenten = 329682;
var Buergeramt_Weissensee_Vorzugstermine = 327284;
// Bezirksamt_Reinickendorf
var Buergeramt_Heiligensee = 122312;
var Buergeramt_Heiligensee_Vorzugstermine = 329763;
var Buergeramt_Maerkisches_Viertel = 122314;
var Buergeramt_Maerkisches_Viertel_Vorzugstermine = 329775;
var Buergeramt_Rathaus_Reinickendorf = 122304;
var Buergeramt_Rathaus_Reinickendorf_Vorzugstermine = 327330;
var Buergeramt_Reinickendorf_Ost = 122311;
var Buergeramt_Reinickendorf_Ost_Vorzugstermine = 327334;
var Buergeramt_Tegel = 122309;
var Buergeramt_Tegel_Vorzugstermine = 327332;
var Mobiles_Buergeramt_Hermsdorf = 317869;
var Mobiles_Buergeramt_Alt_Heiligensee = 350510;
// Bezirksamt_Spandau
var Buergeramt_Rathaus_Spandau = 122281;
var Buergeramt_Rathaus_Spandau_Abholung_von_Dokumenten = 329697;
var Buergeramt_Rathaus_Spandau_Vorzugstermine = 327352;
var Buergeramt_Wasserstadt = 122279;
var Buergeramt_Wasserstadt_Abholung_von_Dokumenten = 329700;
var Buergeramt_Wasserstadt_Vorzugstermine = 329772;
var Buergerbuero_Falkenhagener_Feld = 324414;
var Buergerbuero_Kladow = 122283;
// Bezirksamt_Steglitz_Zehlendorf
var Buergeramt_Lankwitz = 122276;
var Buergeramt_Lankwitz_Vorzugstermine = 327324;
var Buergeramt_Steglitz = 122274;
var Buergeramt_Steglitz_Anwohner_Bewohnerparkausweis = 328245;
var Buergeramt_Steglitz_Vorzugstermine = 327326;
var Buergeramt_Zehlendorf = 122267;
var Buergeramt_Zehlendorf_Vorzugstermine = 329766;
// Bezirksamt_Tempelhof_Schoeneberg
var Buergeramt_Lichtenrade = 122246;
var Buergeramt_Lichtenrade_Abholung_von_Dokumenten = 329866;
var Buergeramt_Lichtenrade_Vorzugstermine = 327318;
var Buergeramt_Schoeneberg = 122251;
var Buergeramt_Schoeneberg_Abholung_von_Dokumenten = 329863;
var Buergeramt_Schoeneberg_Vorzugstermine = 327320;
var Buergeramt_Schoeneberg_Ausbildungsarbeitsplaetze = 327653;
var Buergeramt_Tempelhof = 122257;
var Buergeramt_Tempelhof_Abholung_von_Dokumenten = 329860;
var Buergeramt_Tempelhof_Vorzugstermine = 327322;
var Buergeramt_Tempelhof_Schoeneberg_Backoffice = 327603;
// Bezirksamt_Treptow_Koepenick
var Buergeramt_Koepenick = 122208;
var Buergeramt_Koepenick_Abholung_von_Dokumenten = 330132;
var Buergeramt_Koepenick_Vorzugstermine = 327298;
var Buergeramt_Schoeneweide = 122226;
var Buergeramt_Schoeneweide_Abholung_von_Dokumenten = 330274;
var Buergeramt_Schoeneweide_Vorzugstermine = 327300;
/*******************************/
// List of Anliegen
var Anmelden_einer_Wohnung = 120686;
var Personalausweis_beantragen = 120703;
var Reisepass_beantragen = 121151;
var Gewerbe_anmelden = 121921;
// TODO add more
/*******************************/
// Define here your matter
var anliegenId = Anmelden_einer_Wohnung;
// Define here a list of favored locations
// Vorzugstermine are allowed for matter: Anmelden_einer_Wohnung, Personalausweis_beantragen or Reisepass_beantragen
var dienstleisterlist = [
Buergeramt_1_Kreuzberg_Yorckstrasse,
Buergeramt_1_Kreuzberg_Yorckstrasse_Vorzugstermine,
Buergeramt_3_Friedrichshain_Frankfurter_Allee,
Buergeramt_3_Friedrichshain_Frankfurter_Allee_Vorzugstermine,
Buergeramt_Klosterstrasse_Vorzugstermine,
Buergeramt_Rathaus_Mitte,
Buergeramt_Rathaus_Mitte_Vorzugstermine,
Buergeramt_Rathaus_Tiergarten,
Buergeramt_Rathaus_Tiergarten_Vorzugstermine,
Buergeramt_Blaschkoallee,
Buergeramt_Blaschkoallee_Vorzugstermine,
Buergeramt_Rathaus_Neukoelln,
Buergeramt_Rathaus_Neukoelln_Vorzugstermine,
];
var watingTimeMinutes = 5;
var watingTimeMillis = 1000 * 60 * watingTimeMinutes;
var initialPage =
"https://service.berlin.de/terminvereinbarung/termin/tag.php?termin=1&herkunft=1&" +
"anliegen[]=" +
anliegenId +
"&dienstleisterlist=" +
dienstleisterlist.join(",");
// End of configuration
/*******************************/
function beepEvery5sec() {
var beepSound =
"data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=";
new Audio(beepSound).play();
setTimeout(function () {
beepEvery5sec();
}, 1000 * 5);
}
function errorOnPage(message) {
console.log(message + ", restart search" + waitTimeStr());
beepEvery5sec();
setTimeout(function () {
window.location.href = initialPage;
}, watingTimeMillis);
}
function waitTimeStr() {
var triggerTimeStr = new Date(Date.now() + watingTimeMillis).toTimeString();
return " in " + watingTimeMinutes + "min (" + triggerTimeStr + ")!";
}
function parseHtml() {
console.log("Terminator: start check...");
if (document.querySelectorAll('.alert-error').length > 0) {
console.log("Terminator: Found error, try to handle it");
var msgUseNewSearchParam = "Terminsuche mit dieser Auswahl starten";
var link = Array.from(document.querySelectorAll("a")).find(
(a) => a.textContent === msgUseNewSearchParam
);
if (link) {
link.click();
return;
}
errorOnPage("Terminator: Unknown error");
return;
}
var titleElement = document.querySelector("h1.title")
if (titleElement && titleElement.textContent.search("keine Termine") > 0) {
console.log("Terminator: Nothing found, next try" + waitTimeStr());
setTimeout(function () {
window.location.href = initialPage;
}, watingTimeMillis);
return;
}
var step = document.querySelectorAll('.collapsible-group').length;
if (step == 2) {
var bookableDays = document.querySelectorAll('table .buchbar');
if (bookableDays.length > 0) {
var linkToDayWithFreeDates = bookableDays[0]
.querySelector('a')
.getAttribute('href');
console.log(
"Terminator: found day with free dates " + linkToDayWithFreeDates
);
beepEvery5sec();
window.focus();
window.location.href = linkToDayWithFreeDates;
} else {
console.log("Terminator: Nothing found, next try" + waitTimeStr());
setTimeout(function () {
location.reload(true);
}, watingTimeMillis);
}
return;
}
beepEvery5sec();
if (step == 3) {
var bookableTimeSlots = document.querySelectorAll('table .frei');
if (bookableTimeSlots.length > 0) {
var linkToFreeTime = bookableTimeSlots[bookableTimeSlots.length - 1]
.querySelector('a')
.getAttribute('href');
console.log("Terminator: found free timeslot " + linkToFreeTime);
window.location.href = linkToFreeTime;
} else {
console.log("Terminator: no free timeslots, restart search");
window.location.href = initialPage;
}
return;
}
if (step == 5) {
var dataForm = document.querySelector('form.form-zms');
if (!dataForm) {
console.log("Terminator: Reservation form is missing or broken, restart search");
window.location.href = initialPage;
}
// Personal data form was found
document.querySelector('#familyName').value = name;
document.querySelector('#email').value = email;
document.querySelector('#emailequality').value = email;
document.querySelector('#telephone').value = phoneNumber;
document.querySelector('#agbgelesen').checked = true;
document.getElementsByName('surveyAccepted')[0].value = 0;
console.log("Terminator: Reservation form filled");
if (submit) {
dataForm.submit();
}
return;
}
errorOnPage("Terminator: Unknown state");
}
if (document.readyState == 'loading') {
document.addEventListener('DOMContentLoaded', parseHtml);
} else {
parseHtml();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment