Last active
May 16, 2021 11:14
-
-
Save shredding/e94df43f0abee6cc27f878305b415373 to your computer and use it in GitHub Desktop.
Get notified when there are new slots availale at the IMPFZENTREN in BERLIN
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
import os | |
from datetime import datetime | |
from time import sleep | |
from selenium import webdriver | |
from selenium.common.exceptions import NoSuchElementException, ElementClickInterceptedException | |
""" | |
Installation requirements: | |
$ brew install terminal-notifier | |
$ brew install --cask chromedriver | |
$ pip3 install selenium | |
Change the latest date (open appointments after this date will be ignored) | |
$ python3 run.py | |
""" | |
latest_date = '2021-05-29' | |
urls = [ | |
"https://www.doctolib.de/institut/berlin/ciz-berlin-berlin?pid=practice-158436", | |
"https://www.doctolib.de/institut/berlin/ciz-berlin-berlin?pid=practice-158433", | |
"https://www.doctolib.de/institut/berlin/ciz-berlin-berlin?pid=practice-158435", | |
"https://www.doctolib.de/institut/berlin/ciz-berlin-berlin?pid=practice-158437", | |
"https://www.doctolib.de/institut/berlin/ciz-berlin-berlin?pid=practice-158434", | |
"https://www.doctolib.de/institut/berlin/ciz-berlin-berlin?pid=practice-158431" | |
] | |
waiting_secs = 1 | |
slot = None | |
driver = webdriver.Chrome() | |
current_vaccine = 'moderna' | |
ref_date = datetime.strptime(latest_date, '%Y-%m-%d') | |
def notify(title, message): | |
t = '-title {!r}'.format(title) | |
m = '-message {!r}'.format(message) | |
os.system('terminal-notifier {}'.format(' '.join([m, t]))) | |
def has_warning_message(): | |
try: | |
warning = driver.find_element_by_css_selector('.booking-message-warning span') | |
if 'Aufgrund der starken Nachfrage ist dieses Zentrum nicht mehr verfügbar' in warning.text: | |
return True | |
if 'Sie können für diesen Arzt keine Termine online vereinbaren.' in warning.text: | |
return True | |
if 'Keine Verfügbarkeiten' in warning.text: | |
return True | |
except NoSuchElementException: | |
pass | |
try: | |
warning = driver.find_element_by_css_selector('h1.lang-de') | |
if 'Service vorübergehend außer Betrieb' in warning.text: | |
return True | |
except NoSuchElementException: | |
pass | |
try: | |
elem = driver.find_element_by_css_selector('.dl-modal-body-normal') | |
# doctolib was out of sync, the slot didn't really exist | |
if "Bitte wählen Sie einen neuen Termin aus." in elem.text: | |
return True | |
if "Es ist während der Terminvereinbarung ein Fehler aufgetreten." in elem.text: | |
return True | |
except NoSuchElementException: | |
pass | |
return False | |
def can_progress_because_element_available(css_selector, multiple_elements=False, callback=None): | |
for _ in range(10): | |
if has_warning_message(): | |
return False | |
try: | |
if multiple_elements: | |
elem = driver.find_elements_by_css_selector(css_selector)[0] | |
else: | |
elem = driver.find_element_by_css_selector(css_selector) | |
if callback is None: | |
elem.click() | |
return True | |
elif callback(elem): | |
elem.click() | |
return True | |
except (NoSuchElementException, IndexError): | |
sleep(waiting_secs) | |
except ElementClickInterceptedException: | |
continue | |
return False | |
def validate_time(elem): | |
txt = elem.text | |
txt = txt.replace('Nächster Termin am ', '').replace(' Mai ', '05.').replace(' Juni ', '06.').replace(' Juli ', '07.').replace('August ', '08.') | |
txt = txt.replace(' ', '') | |
if not len(txt) == 10: | |
txt = '0' + txt | |
if datetime.strptime(txt, '%d.%m.%Y') <= ref_date: | |
return True | |
print(f'Found slot, but after requested date: {txt}.') | |
return False | |
while slot is None: | |
# we alternate between moderna and biontech selection every round to make things easier | |
current_vaccine = 'biontech' if current_vaccine == 'moderna' else 'moderna' | |
for url in urls: | |
print(f'Checking slots for {url} ...') | |
driver.get(url) | |
sleep(waiting_secs) | |
try: | |
driver.find_element_by_id('didomi-notice-agree-button').click() | |
except NoSuchElementException: | |
pass | |
try: | |
driver.find_element_by_css_selector('.dl-select-block').click() | |
options = driver.find_elements_by_css_selector('.dl-select-block option') | |
is_moderna_only = False | |
for option in options: | |
if option.text == 'Wählen sie den Besuchsgrund': | |
continue | |
if 'astra' in option.text.lower(): | |
is_moderna_only = True | |
elif current_vaccine in option.text.lower(): | |
option.click() | |
break | |
elif is_moderna_only and 'moderna' in option.text.lower(): | |
option.click() | |
except NoSuchElementException: | |
pass | |
if not can_progress_because_element_available('.availabilities-message span', callback=validate_time): | |
continue | |
if not can_progress_because_element_available('.availabilities-next-slot', multiple_elements=True): | |
continue | |
slot = url | |
break | |
notify('New slots available!', 'Get your slot now!') | |
print(f'Available slots in {slot}.') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment