diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..73f69e09
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/aws.xml b/.idea/aws.xml
new file mode 100644
index 00000000..b63b642c
--- /dev/null
+++ b/.idea/aws.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/doctoshotgun.iml b/.idea/doctoshotgun.iml
new file mode 100644
index 00000000..4f2c9af6
--- /dev/null
+++ b/.idea/doctoshotgun.iml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 00000000..105ce2da
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..86561143
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..4278ec1e
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doctoshotgun.py b/doctoshotgun.py
index 8e9096ef..1e4e0a45 100755
--- a/doctoshotgun.py
+++ b/doctoshotgun.py
@@ -35,6 +35,7 @@
try:
from playsound import playsound as _playsound, PlaysoundException
+
def playsound(*args):
try:
return _playsound(*args)
@@ -107,7 +108,7 @@ def get_next_page(self):
# JavaScript:
# var t = (e = r()(e)).data("u")
# , n = atob(t.replace(/\s/g, '').split('').reverse().join(''));
-
+
import base64
href = base64.urlsafe_b64decode(''.join(span.attrib['data-u'].split())[::-1]).decode()
query = dict(parse.parse_qsl(parse.urlsplit(href).query))
@@ -121,9 +122,10 @@ def get_next_page(self):
if 'page' in query:
return int(query['page'])
-
+
return None
+
class CenterResultPage(JsonPage):
pass
@@ -162,8 +164,8 @@ def get_agenda_ids(self, motive_id, practice_id=None):
agenda_ids = []
for a in self.doc['data']['agendas']:
if motive_id in a['visit_motive_ids'] and \
- not a['booking_disabled'] and \
- (not practice_id or a['practice_id'] == practice_id):
+ not a['booking_disabled'] and \
+ (not practice_id or a['practice_id'] == practice_id):
agenda_ids.append(str(a['id']))
return agenda_ids
@@ -250,7 +252,8 @@ def __init__(self, *args, **kwargs):
self.session.headers['sec-fetch-dest'] = 'document'
self.session.headers['sec-fetch-mode'] = 'navigate'
self.session.headers['sec-fetch-site'] = 'same-origin'
- self.session.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'
+ self.session.headers[
+ 'User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'
self.patient = None
@@ -259,8 +262,9 @@ def do_login(self, code):
self.open(self.BASEURL + '/sessions/new')
except ServerError as e:
if e.response.status_code in [503] \
- and 'text/html' in e.response.headers['Content-Type'] \
- and ('cloudflare' in e.response.text or 'Checking your browser before accessing' in e .response.text):
+ and 'text/html' in e.response.headers['Content-Type'] \
+ and (
+ 'cloudflare' in e.response.text or 'Checking your browser before accessing' in e.response.text):
log('Request blocked by CloudFlare', color='red')
if e.response.status_code in [520]:
log('Cloudflare is unable to connect to Doctolib server. Please retry later.', color='red')
@@ -279,7 +283,9 @@ def do_login(self, code):
print("Requesting 2fa code...")
if not code:
if not sys.__stdin__.isatty():
- log("Auth Code input required, but no interactive terminal available. Please provide it via command line argument '--code'.", color='red')
+ log(
+ "Auth Code input required, but no interactive terminal available. Please provide it via command line argument '--code'.",
+ color='red')
return False
self.send_auth_code.go(
json={'two_factor_auth_method': 'email'}, method="POST")
@@ -299,12 +305,12 @@ def find_centers(self, where, motives=None, page=1):
for city in where:
try:
self.centers.go(where=city, params={
- 'ref_visit_motive_ids[]': motives, 'page': page})
+ 'ref_visit_motive_ids[]': motives, 'page': page})
except ServerError as e:
if e.response.status_code in [503]:
if 'text/html' in e.response.headers['Content-Type'] \
- and ('cloudflare' in e.response.text or
- 'Checking your browser before accessing' in e .response.text):
+ and ('cloudflare' in e.response.text or
+ 'Checking your browser before accessing' in e.response.text):
log('Request blocked by CloudFlare', color='red')
return
if e.response.status_code in [520]:
@@ -359,7 +365,8 @@ def try_to_book(self, center, vaccine_list, start_date, end_date, only_second, o
motives_id = dict()
for vaccine in vaccine_list:
motives_id[vaccine] = self.page.find_motive(
- r'.*({})'.format(vaccine), singleShot=(vaccine == self.vaccine_motives[self.KEY_JANSSEN] or only_second or only_third))
+ r'.*({})'.format(vaccine),
+ singleShot=(vaccine == self.vaccine_motives[self.KEY_JANSSEN] or only_second or only_third))
motives_id = dict((k, v)
for k, v in motives_id.items() if v is not None)
@@ -379,12 +386,14 @@ def try_to_book(self, center, vaccine_list, start_date, end_date, only_second, o
# do not filter to give a chance
agenda_ids = center_page.get_agenda_ids(motive_id)
- if self.try_to_book_place(profile_id, motive_id, practice_id, agenda_ids, vac_name.lower(), start_date, end_date, only_second, only_third, dry_run):
+ if self.try_to_book_place(profile_id, motive_id, practice_id, agenda_ids, vac_name.lower(), start_date,
+ end_date, only_second, only_third, dry_run):
return True
return False
- def try_to_book_place(self, profile_id, motive_id, practice_id, agenda_ids, vac_name, start_date, end_date, only_second, only_third, dry_run=False):
+ def try_to_book_place(self, profile_id, motive_id, practice_id, agenda_ids, vac_name, start_date, end_date,
+ only_second, only_third, dry_run=False):
date = start_date.strftime('%Y-%m-%d')
while date is not None:
self.availabilities.go(
@@ -435,9 +444,9 @@ def try_to_book_place(self, profile_id, motive_id, practice_id, agenda_ids, vac_
log(' ├╴ Best slot found: %s', parse_date(
slot_date_first).strftime('%c'))
- appointment = {'profile_id': profile_id,
+ appointment = {'profile_id': profile_id,
'source_action': 'profile',
- 'start_date': slot_date_first,
+ 'start_date': slot_date_first,
'visit_motive_ids': str(motive_id),
}
@@ -548,6 +557,33 @@ def try_to_book_place(self, profile_id, motive_id, practice_id, agenda_ids, vac_
return self.page.doc['confirmed']
+class DoctolibFR(Doctolib):
+ BASEURL = 'https://www.doctolib.fr'
+ KEY_PFIZER = '6970'
+ KEY_PFIZER_SECOND = '6971'
+ KEY_PFIZER_THIRD = None
+ KEY_MODERNA = '7005'
+ KEY_MODERNA_SECOND = '7004'
+ KEY_MODERNA_THIRD = None
+ KEY_JANSSEN = '7945'
+ KEY_ASTRAZENECA = '7107'
+ KEY_ASTRAZENECA_SECOND = '7108'
+ vaccine_motives = {
+ KEY_PFIZER: 'Pfizer',
+ KEY_PFIZER_SECOND: '2de.*Pfizer',
+ KEY_PFIZER_THIRD: '3e.*Pfizer',
+ KEY_MODERNA: 'Moderna',
+ KEY_MODERNA_SECOND: '2de.*Moderna',
+ KEY_MODERNA_THIRD: '3e.*Moderna',
+ KEY_JANSSEN: 'Janssen',
+ KEY_ASTRAZENECA: 'AstraZeneca',
+ KEY_ASTRAZENECA_SECOND: '2de.*AstraZeneca',
+ }
+
+ centers = URL(r'/vaccination-covid-19/(?P\w+)', CentersPage)
+ center = URL(r'/centre-de-sante/.*', CenterPage)
+
+
class DoctolibDE(Doctolib):
BASEURL = 'https://www.doctolib.de'
KEY_PFIZER = '6768'
@@ -574,33 +610,6 @@ class DoctolibDE(Doctolib):
center = URL(r'/praxis/.*', CenterPage)
-class DoctolibFR(Doctolib):
- BASEURL = 'https://www.doctolib.fr'
- KEY_PFIZER = '6970'
- KEY_PFIZER_SECOND = '6971'
- KEY_PFIZER_THIRD = '8192'
- KEY_MODERNA = '7005'
- KEY_MODERNA_SECOND = '7004'
- KEY_MODERNA_THIRD = '8193'
- KEY_JANSSEN = '7945'
- KEY_ASTRAZENECA = '7107'
- KEY_ASTRAZENECA_SECOND = '7108'
- vaccine_motives = {
- KEY_PFIZER: 'Pfizer',
- KEY_PFIZER_SECOND: '2de.*Pfizer',
- KEY_PFIZER_THIRD: '3e.*Pfizer',
- KEY_MODERNA: 'Moderna',
- KEY_MODERNA_SECOND: '2de.*Moderna',
- KEY_MODERNA_THIRD: '3e.*Moderna',
- KEY_JANSSEN: 'Janssen',
- KEY_ASTRAZENECA: 'AstraZeneca',
- KEY_ASTRAZENECA_SECOND: '2de.*AstraZeneca',
- }
-
- centers = URL(r'/vaccination-covid-19/(?P\w+)', CentersPage)
- center = URL(r'/centre-de-sante/.*', CenterPage)
-
-
class Application:
@classmethod
def create_default_logger(cls):
@@ -688,7 +697,8 @@ def main(self, cli_args=None):
patients = docto.get_patients()
if len(patients) == 0:
- print("It seems that you don't have any Patient registered in your Doctolib account. Please fill your Patient data on Doctolib Website.")
+ print(
+ "It seems that you don't have any Patient registered in your Doctolib account. Please fill your Patient data on Doctolib Website.")
return 1
if args.patient >= 0 and args.patient < len(patients):
docto.patient = patients[args.patient]
@@ -830,7 +840,8 @@ def main(self, cli_args=None):
log('Center %(name_with_title)s (%(city)s):' % center)
- if docto.try_to_book(center, vaccine_list, start_date, end_date, args.only_second, args.only_third, args.dry_run):
+ if docto.try_to_book(center, vaccine_list, start_date, end_date, args.only_second, args.only_third,
+ args.dry_run):
log('')
log('💉 %s Congratulations.' %
colored('Booked!', 'green', attrs=('bold',)))
@@ -839,7 +850,8 @@ def main(self, cli_args=None):
sleep(SLEEP_INTERVAL_AFTER_CENTER)
log('')
- log('No free slots found at selected centers. Trying another round in %s sec...', SLEEP_INTERVAL_AFTER_RUN)
+ log('No free slots found at selected centers. Trying another round in %s sec...',
+ SLEEP_INTERVAL_AFTER_RUN)
sleep(SLEEP_INTERVAL_AFTER_RUN)
except CityNotFound as e:
print('\n%s: City %s not found. Make sure you selected a city from the available countries.' % (