Skip to content
This repository has been archived by the owner on Jul 9, 2024. It is now read-only.

Commit

Permalink
Improve selenium vcode generation (#480)
Browse files Browse the repository at this point in the history
* Add second try if "Es ist ein unerwarteter Fehler aufgetreten"

* Improve selenium_code_anfordern

* Close browser if error occurs

* Fixed unable to locate element

* Fix unable to locate element #2

* Fix 'CLogger' object has no attribute 'warning'

* Prevent unable locate sms_verifizierung_h1 after successful request

* Cleaned up main

Co-authored-by: Jonas Mock <[email protected]>
  • Loading branch information
jonasmock and Jonas Mock authored Jun 15, 2021
1 parent f2c0f57 commit 995824b
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 76 deletions.
21 changes: 3 additions & 18 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,30 +378,15 @@ def gen_code(kontaktdaten):
print("Das Datum entspricht nicht dem richtigen Format (DD.MM.YYYY). "
"Bitte erneut versuchen.")

print()
# code anfordern
# code anfordern via selenium
try:
token, cookies = its.selenium_code_anfordern(
mail, telefonnummer, plz_impfzentrum, geburtsdatum)
if its.selenium_code_anfordern(mail, telefonnummer, plz_impfzentrum, geburtsdatum):
return True
except RuntimeError as exc:
print(
f"\nDie Code-Generierung war leider nicht erfolgreich:\n{str(exc)}")
return False

# code bestätigen
print("\nDu erhältst gleich eine SMS mit einem Code zur Bestätigung deiner Telefonnummer.\n"
"Trage diesen hier ein. Solltest du dich vertippen, hast du noch 2 weitere Versuche.\n"
"Beispiel: 123-456")

# 3 Versuche für die SMS-Code-Eingabe
for _ in range(3):
sms_pin = input("\n> SMS-Code: ").replace("-", "")
print()
if its.code_bestaetigen(token, cookies, sms_pin, plz_impfzentrum):
print("\nDu kannst jetzt mit der Terminsuche fortfahren.")
return True
print("\nSMS-Code ungültig")

print("Die Code-Generierung war leider nicht erfolgreich.")
return False

Expand Down
159 changes: 101 additions & 58 deletions tools/its.py
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,7 @@ def selenium_code_anfordern(self, mail, telefonnummer,
driver.get(f"{url}impftermine/service?plz={plz_impfzentrum}")
driver.refresh()


# ets-session-its-cv-quick-check im SessionStorage setzen um verfügbare Termine zu simulieren
ets_session_its_cv_quick_check = '{"birthdate":"'+ data["birthday"] +'","slotsAvailable":{"pair":true,"single":false}}'
driver.execute_script('window.sessionStorage.setItem("ets-session-its-cv-quick-check",\''+ ets_session_its_cv_quick_check +'\');')
Expand All @@ -1053,13 +1054,26 @@ def selenium_code_anfordern(self, mail, telefonnummer,
driver.get(f"{url}impftermine/check")
self.log.info("Überprüfung der Impfberechtigung übersprungen / Vorhandene Termine simuliert und impftermine/check geladen.")

time.sleep(1)

# Anpassen der HTML elemente im Browser um Nutzer aktuellen Status anzuzeigen
check_h1_xpath = "//app-its-check-success//h1"
check_h1 = driver.find_element_by_xpath(check_h1_xpath)
driver.execute_script("arguments[0].setAttribute('style','color: #FF0000;font-weight: bold; font-size: 35px;')", check_h1)
driver.execute_script(f"arguments[0].innerText='Vaccipy! - Bitte nichts eingeben oder anklicken.'", check_h1)
check_p_xpath = "//app-its-check-success//p"
check_p = driver.find_element_by_xpath(check_p_xpath)
driver.execute_script("arguments[0].setAttribute('style','font-weight: bold; font-size: 25px;')", check_p)

# random start position
current_mouse_positon = (randint(1,driver.get_window_size()["width"]-1),
randint(1,driver.get_window_size()["height"]-1))

# Simulation der Mausbewegung
driver.execute_script(f"arguments[0].innerText='Status: Maussimulation nach x:{current_mouse_positon[0]}, y:{current_mouse_positon[1]}'", check_p)
current_mouse_positon = move_mouse_to_coordinates(self.log, 0, 0, current_mouse_positon[0],
current_mouse_positon[1], driver)


# Klick auf "Auswahl bestätigen" im Cookies-Banner
button_xpath = "//a[contains(@class,'cookies-info-close')][1]"
Expand All @@ -1069,11 +1083,12 @@ def selenium_code_anfordern(self, mail, telefonnummer,

# Simulation der Mausbewegung
element = driver.find_element_by_xpath(button_xpath)
driver.execute_script(f"arguments[0].innerText='Status: Maussimulation nach x: {element.location['x']}, y: {element.location['y']}'", check_p)
current_mouse_positon = move_mouse_to_coordinates(self.log, current_mouse_positon[0],
current_mouse_positon[1],
element.location['x'],
element.location['y'], driver)

driver.execute_script(f"arguments[0].innerText='Status: Cookie-Banner Anklicken'", check_p)
action.click(button).perform()
time.sleep(0.5)

Expand All @@ -1085,14 +1100,15 @@ def selenium_code_anfordern(self, mail, telefonnummer,

# Simulation der Mausbewegung
element = driver.find_element_by_xpath(input_xpath)
driver.execute_script(f"arguments[0].innerText='Status: Maussimulation nach x: {element.location['x']}, y: {element.location['y']}'", check_p)
current_mouse_positon = move_mouse_to_coordinates(self.log, current_mouse_positon[0],
current_mouse_positon[1],
element.location['x'],
element.location['y'], driver)

action.move_to_element(input_field).click().perform()

# Chars einzeln eingeben mit kleiner Pause
driver.execute_script(f"arguments[0].innerText='Status: E-Mail wird eingegeben'", check_p)
for char in data['email']:
input_field.send_keys(char)
time.sleep(randint(500,1000)/1000)
Expand All @@ -1108,14 +1124,15 @@ def selenium_code_anfordern(self, mail, telefonnummer,

# Simulation der Mausbewegung
element = driver.find_element_by_xpath(input_xpath)
driver.execute_script(f"arguments[0].innerText='Status: Maussimulation nach x: {element.location['x']}, y: {element.location['y']}'", check_p)
current_mouse_positon = move_mouse_to_coordinates(self.log, current_mouse_positon[0],
current_mouse_positon[1],
element.location['x'],
element.location['y'], driver)

action.move_to_element(input_field).click().perform()

# Chars einzeln eingeben mit kleiner Pause
driver.execute_script(f"arguments[0].innerText='Status: Phone wird eingegeben'", check_p)
for char in data['phone'][3:]:
input_field.send_keys(char)
time.sleep(randint(500,1000)/1000)
Expand All @@ -1130,71 +1147,97 @@ def selenium_code_anfordern(self, mail, telefonnummer,

# Simulation der Mausbewegung
element = driver.find_element_by_xpath(button_xpath)
driver.execute_script(f"arguments[0].innerText='Status: Maussimulation nach x: {element.location['x']}, y: {element.location['y']}'", check_p)
current_mouse_positon = move_mouse_to_coordinates(self.log, current_mouse_positon[0],
current_mouse_positon[1],
element.location['x'],
element.location['y'], driver)
driver.execute_script(f"arguments[0].innerText='Status: Versuche Anfrage abzuschicken'", check_p)
action.move_to_element(button).click().perform()

action.click(button).perform()
self.log.info("SMS-Anfrage an Server versandt.")
# Zweiter Klick-Versuch, falls Meldung "Es ist ein unerwarteter Fehler aufgetreten" erscheint
# Falls eine andere Meldung aufgetrteten ist -> Abbruch
try:
answer_xpath = "//app-its-check-success//span[@class=\"text-pre-wrap\"]"
time.sleep(0.5)
element = driver.find_element_by_xpath(answer_xpath)
except Exception as e:
element = None

if element:
if element.text == "Es ist ein unerwarteter Fehler aufgetreten":
driver.execute_script(f"arguments[0].innerText='Status: Zweiter Versuch Anfrage abzuschicken'", check_p)
action.move_to_element(button).click().perform()
elif element.text == "Anfragelimit erreicht.":
driver.close()
raise RuntimeError("Anfragelimit erreicht")
elif element.text == "Geburtsdatum ungueltig oder in der Zukunft":
driver.close()
raise RuntimeError("Geburtsdatum ungueltig oder in der Zukunft")

# Cookies auslesen / Muss nicht über den langen river_enter_code() weg gemacht werden
cookies = driver.get_cookies()
required = ["bm_sz", "akavpau_User_allowed"]
optional = ["bm_sv", "bm_mi", "ak_bmsc", "_abck"]
cookies = {
c["name"]: c["value"]
for c in cookies
if c["name"] in required or c["name"] in optional
}

res = None
max_retry = 5
retry_counter = 0
while not res:
# Get response von rest/smspin/anforderung
for request in driver.requests:
if request.url == location:
res = request.response
break
if retry_counter >= max_retry:
raise RuntimeError("Keine Antwort vom Server erhalten.")
else:
retry_counter += 1
self.log.info(f"Warten auf Antwort des Servers. Kann einige Sekunden dauern. Versuch {retry_counter} von {max_retry}")
time.sleep(5)

# Browser wird nicht mehr benötigt, SMS-Code wird in Vaccipy eingegeben
driver.close()
time.sleep(2)

if res.status_code == 429:
self.log.error("Anfrage wurde von der Botprotection geblockt")
self.log.error("Erneuter versuch in 30 Sekunden")
time.sleep(30)
continue
# Prüfen ob SMS Verifizierung geladen wurde falls nicht Abbruch
sms_verifizierung_h1_xpath = "//app-page-its-check-result//h1"
sms_verifizierung_h1 = driver.find_element_by_xpath(sms_verifizierung_h1_xpath)
if sms_verifizierung_h1.text != "SMS Verifizierung":
driver.close()
raise RuntimeError("Vermittlungscode kann derzeit nicht angefragt werden. Versuchen Sie es später erneut.")

if res.status_code == 400:
# Ab jetzt befinden wir uns auf der SMS Verifizierung Seite
location = f"{url}rest/smspin/verifikation"
self.log.info("SMS-Anfrage an Server versandt.")
self.log.info("Bitte SMS-Code innerhalb der nächsten 60 Sekunden im Browser-Fenster eingeben.")

# 90 Sekunden lang auf Antwort vom Server warten
# Eventuell gibt User seinen Pin falsch ein etc.
max_sms_code_eingabe_sekunden = 90
while max_sms_code_eingabe_sekunden:

# Verbleibende Zeit anzeigen
try:
# error Laden
error = json.loads(res.body.decode('utf-8'))['error']
except JSONDecodeError as exc:
raise RuntimeError(f"JSONDecodeError: {str(exc)}") from exc
print(error)
# Spezifischen Fehlermeldung ausgeben
if error == 'Geburtsdatum ungueltig oder in der Zukunft':
raise RuntimeError("Geburtsdatum ungueltig oder in der Zukunft")
elif error == 'Anfragelimit erreicht.':
raise RuntimeError("Anfragelimit erreicht")
else:
raise RuntimeError("Unbekannter Fehler beim Anfordern des SMS-Codes aufgetreten.")
driver.execute_script(f"arguments[0].innerText='Vaccipy! - Bitte SMS-Code im Browser eingeben. Noch {max_sms_code_eingabe_sekunden} Sekunden verbleibend.'", sms_verifizierung_h1)
except Exception as e:
pass

try:
# Token laden
token = json.loads(res.body.decode('utf-8'))['token']
except JSONDecodeError as exc:
raise RuntimeError(f"JSONDecodeError: {str(exc)}") from exc

return (token, cookies)
# Alle bisherigen Requests laden
sms_verification_responses= []
for request in driver.requests:
if request.url == location:
sms_verification_responses.append(request.response)

if sms_verification_responses:

# Neuste Antowrt vom Server auslesen
# User kann z.B 2 mal den Pin falsch eingeben uns interessiert nur die neuste Antwort vom Server
latest_reponse = sms_verification_responses[-1]

if latest_reponse is not None:
if latest_reponse.status_code == 400:
try:
# error Laden
error = json.loads(latest_reponse.body.decode('utf-8'))['error']
except JSONDecodeError as exc:
raise RuntimeError(f"JSONDecodeError: {str(exc)}") from exc

if error == "Pin ungültig":
self.log.info("Der eingegebene SMS-Code ist ungültig.")

elif latest_reponse.status_code == 200:
self.log.info(f"SMS-Code erfolgreich übermittelt. Bitte Prüfen Sie Ihre E-Mails.")
driver.close()
return True
elif latest_reponse.status_code == 429:
driver.close()
raise RuntimeError("SMS-Code konnte nicht übermittelt werden. Blockiert durch Botprotection.")

time.sleep(1)
max_sms_code_eingabe_sekunden -= 1


driver.close()
self.log.info(f"SMS-Verifikation nicht innerhalb von 90 Sekunden abgeschlossen. Versuchen Sie es später erneut.")
return False


def code_bestaetigen(self, token, cookies, sms_pin, plz_impfzentrum):
Expand Down

0 comments on commit 995824b

Please sign in to comment.