#!/usr/bin/env python3 import configparser import argparse import requests from sys import exit import logging import coloredlogs import json import re from io import StringIO import getpass import os from zoneinfo import ZoneInfo from datetime import datetime from math import floor from ics import Calendar, Event def getKey(dictionnary, key): try: return dictionnary[key] except: logger.error('Missing key: %s' % key) exit(-1) def main(): logger = logging.getLogger(__name__) coloredlogs.install() parser = argparse.ArgumentParser() parser.add_argument("-l", "--login", dest='login', type=str, required=False, help="Username or login (usually a phone number with international prefix).") parser.add_argument("-C", "--city", dest='city', type=str, required=False, help="City.") parser.add_argument("-c", "--config", dest='configFileName', required=False, default=None, help="Configuration file.") parser.add_argument("-p", "--password", dest='password', nargs='?', required=False, default=None, help="Password.") parser.add_argument("-o", "--output", dest='calendar', required=True, default='cal.ics', help="Output calendar file.") parser.add_argument("-d", "--debug", dest='debug', action='store_true', required=False, help="Activate debug.") args = parser.parse_args() logger.info("Initial arguments: %s" % args) if args.debug: logger.info('Setting logging to debug mode') coloredlogs.set_level(level=logging.DEBUG) if args.configFileName != None: try: configFile = open(args.configFileName, 'r') except Exception as e: logger.info('Impossible to open configuration file. Error: %s' % e) exit(-1) config = configparser.ConfigParser() config.read_file(configFile) sections = config.sections() if 'Login' in sections: options = config.options('Login') if 'login' in options: args.login = config.get('Login','login') if 'password' in options: args.password = config.get('Login','password') logger.info("Final arguments: %s" % args) if args.login == None: logger.info('Login must be provided.') parser.print_help() exit(-1) if args.password == None: args.password = getpass.getpass() # Récupération des cookies de base baseURL = 'https://www.espace-citoyens.net' url = baseURL + '/betton/espace-citoyens' logger.info('Retrieve base site') html = requests.get(url, allow_redirects=False) if html.status_code != 200: logger.info('Impossible to retrieve Web site: %d' % html.status_code) cookies = html.cookies found = False p = re.compile('^.*
Accueil de loisirs mercredi<.*$') content = StringIO(html.content.decode('utf8')) for line in content.readlines(): m = p.match(line) if m != None: if not foundCategory: # If we found the kind of reservation we change the regexp p = re.compile('^.*href="(?P[^"]+)".*fleche.png".*$') foundCategory = True else: found = True resa = baseURL+m.group('url') break if found: logger.debug("Found mercredi: %s" % resa) else: logger.error("Impossible to find accueil du mercredi") exit(-1) html = requests.get(resa, cookies=cookies, allow_redirects=False) if html.status_code != 200: logger.info('Impossible to retrieve reservation page: %d' % html.status_code) variables = ['idPer', 'idIns', 'idLie', 'idClg'] values = {} for var in variables: found = False content = StringIO(html.content.decode('utf8')) p = re.compile('^.*var %s = (?P[0-9]+).*$' % var) for line in content.readlines(): m = p.match(line) if m != None: found = True value = int(m.group('value')) values[var] = value break if not found: logger.error('Impossible to find value for variable: %s' % var) exit(-1) else: logger.debug('Found value for var %s: %d' % (var, value)) calendar = requests.get(baseURL+'/betton/espace-citoyens/DemandeEnfance/NouvelleDemandeReservationGetCalendrier', params=values, cookies=cookies) if calendar.status_code != 200: logger.info('Impossible to retrieve calendar: %d' % html.status_code) calendar = json.load(StringIO(calendar.content.decode('utf8'))) weeks = getKey(calendar, 'listeSemainesAffichees') typeResas = getKey(calendar, 'listeUnitesInscr') dictResa = {} for typeResa in typeResas: idResa = getKey(typeResa, 'idUnite') codeResa = getKey(typeResa, 'codeUnite') descResa = getKey(typeResa, 'libUnite') dictResa[idResa] = (descResa, codeResa) cal = Calendar() for week in weeks: numSemaine = int(getKey(week, 'numSemaine')) days = getKey(week, 'listeJoursAffiches') for day in days: resas = getKey(day, 'listeUnitesJour') date = int(getKey(day, 'idJour')) year = int(date/10000) month = int((date - year*10000) / 100) day = date - year*10000 - month*100 for resa in resas: checked = getKey(resa, 'nbConsoBase') != None if checked: typeResa = getKey(resa, 'idUnite') e = Event() e.name = dictResa[typeResa][0] code = dictResa[typeResa][1] if 'Matin' in code: begin = datetime(year,month,day,7,30,0, tzinfo=ZoneInfo("Europe/Paris")) end = datetime(year,month,day,12,0,0, tzinfo=ZoneInfo("Europe/Paris")) elif 'AM' in code: begin = datetime(year,month,day,13,30,0, tzinfo=ZoneInfo("Europe/Paris")) end = datetime(year,month,day,18,0,0, tzinfo=ZoneInfo("Europe/Paris")) elif 'Repas' in code: begin = datetime(year,month,day,12,00,0, tzinfo=ZoneInfo("Europe/Paris")) end = datetime(year,month,day,13,30,0, tzinfo=ZoneInfo("Europe/Paris")) else: logger.error('Impossible to determine the type of reservation: %s' % code) exit(-1) e.begin = begin e.end = end cal.events.add(e) with open(args.calendar, 'w') as f: f.writelines(cal.serialize_iter()) if __name__ == "__main__": main()