google calendar

Signed-off-by: Omar Sánchez Pizarro <omar.sanchez@pistacero.net>
This commit is contained in:
Omar Sánchez Pizarro
2022-10-25 13:10:55 +02:00
parent 321f88f16a
commit 73dc43a193
4 changed files with 108 additions and 25 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
config.json config.json
googleoauth.json

View File

@@ -2,24 +2,25 @@ Autoficher para Timenet
============================== ==============================
Que es esto? Que es esto?
------------- ---------------
Aplicación simple para fichar y desfichar automáticamente Aplicación simple para fichar y desfichar automáticamente
Como se usa? Como se usa?
--------------- ---------------
1. Ejecutamos `pip3 install -r requirements.txt` para instalar los componentes 1. Ejecutamos `pip3 install -r requirements.txt` para instalar los componentes
2. Ejecutamos `python3 ./autofitcher.py -u <user> -p <pin> [-t <0 = Entrada, 1 = Salida>| -bt]` 2. Ejecutamos `python3 ./autofitcher.py -u <user> -p <pin> [-t <0 = Entrada, 1 = Salida>| -bt]`
3. Opcionalmente podemos ponerlo en un Crontab de linux para que se ejecute cada X tiempo y se realize el fichado automatico 3. Opcionalmente podemos ponerlo en un Crontab de linux para que se ejecute cada X tiempo y se realize el fichado automatico
Parametros obligatorios Parametros obligatorios
--------------- ---------------
- -u > Usuario de timenet (Pista Cero = 4c26cc59-ee52-4c47-b7fe-1065a5e5bf84) (Lo he pasado a config.json) - -u > Usuario de timenet (Pista Cero = 4c26cc59-ee52-4c47-b7fe-1065a5e5bf84) (Lo he pasado a config.json)
- -p > Pin de usuario - -p > Pin de usuario
Tendremos que escojer también entre --basedtime o --type Tendremos que escojer también entre --basedtime o --type
- -bt > No requiere argumento. Esto ficha y desficha basado en la configuración de horas a seguir - -bt > No requiere argumento. Esto ficha y desficha basado en la configuración de horas a seguir
- -t <0 = Entrada, 1 = Salida> > Forzamos estado al entrar/salir - -t <0 = Entrada, 1 = Salida> > Forzamos estado al entrar/salir
Calendario google
---------------
Para añadir el uso de calendario de google debemos de descargar el archivo de acceso oauth API y añadirlo
a la raiz del proyecto como "googleoauth.json" se revisará calendario de festivos en españa y calendario autoficher.

View File

@@ -16,13 +16,21 @@
import json import json
from datetime import datetime from datetime import datetime
import os import os
import pytz import pytz
import requests import requests
import argparse import argparse
import telegram import telegram
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
from dateutil.relativedelta import relativedelta
# Introducimos los argumentos obligatorios y opcionales # Introducimos los argumentos obligatorios y opcionales
parser = argparse.ArgumentParser(add_help=False) parser = argparse.ArgumentParser(add_help=False)
parser._action_groups.pop() parser._action_groups.pop()
@@ -45,6 +53,11 @@ optionalArgs.add_argument('-ga', '--geoAccuracy', help="GEO Accuracy", type=floa
args = parser.parse_args() args = parser.parse_args()
if not args.type and not args.basedtime:
print("Hay que indicar al menos un metodo de fichaje -t <0 o 1> o -bt")
exit(20)
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class AutoFicher(): class AutoFicher():
# URL de la api # URL de la api
@@ -59,7 +72,7 @@ class AutoFicher():
try: try:
self.loadConfig() self.loadConfig()
except: except:
print("No se ha podido cargar el archivo de configuración") self.sendReport("No se ha podido cargar el archivo de configuración")
exit(20) exit(20)
def loadConfig(self): def loadConfig(self):
@@ -82,6 +95,7 @@ class AutoFicher():
self.config['geo']['accuracy'] = args.geoAccuracy self.config['geo']['accuracy'] = args.geoAccuracy
def sendReport(self, message): def sendReport(self, message):
print(message)
bot = telegram.Bot(token=self.config['telegram']['token']) bot = telegram.Bot(token=self.config['telegram']['token'])
bot.sendMessage(chat_id=self.config['telegram']['chat_id'], text=str(message)) bot.sendMessage(chat_id=self.config['telegram']['chat_id'], text=str(message))
@@ -110,6 +124,11 @@ class AutoFicher():
def sendUpdate(self): def sendUpdate(self):
self.updateTime() self.updateTime()
calendar = self.calendarCheck()
if calendar:
self.sendReport("🟥🕐 Comprobación de calendario: " + calendar)
return
typ = 0 typ = 0
if args.basedtime and not args.type: if args.basedtime and not args.type:
hour = datetime.now().strftime("%H") hour = datetime.now().strftime("%H")
@@ -123,8 +142,7 @@ class AutoFicher():
elif hour in hOUT: elif hour in hOUT:
typ = 1 typ = 1
else: else:
print( self.sendReport("No se ha fichado ni desfichado ya que estamos en horario laboral, fuerze el estado con el parametro --type <0 = Entrada, 1 = Salida>")
"No se ha fichado ni desfichado ya que estamos en horario laboral, fuerze el estado con el parametro --type <0 = Entrada, 1 = Salida>")
else: else:
typ = args.type typ = args.type
@@ -142,38 +160,98 @@ class AutoFicher():
response = None response = None
if not args.debug: if not args.debug:
print("####HACIENDO CHECK####") self.sendReport("####HACIENDO CHECK####")
response = self.post("check", data, self.headers) response = self.post("check", data, self.headers)
else:
self.sendReport('Corriendo en modo debug. No se realizará ninguna acción')
exit(20)
try: try:
rj = json.loads(response.text) rj = json.loads(response.text)
except: except:
print("La respuesta al hacer check no es correcta... algo ha pasado :/") self.sendReport("La respuesta al hacer check no es correcta... algo ha pasado :/")
exit(20) exit(20)
if args.debug:
self.sendReport('Corriendo en modo debug. No se realizará ninguna acción')
if response.status_code == 200: if response.status_code == 200:
if rj['Repeated']: if rj['Repeated']:
time = datetime.strptime(rj['RepeatedTime'], "%Y-%m-%dT%H:%M:%SZ").replace( time = datetime.strptime(rj['RepeatedTime'], "%Y-%m-%dT%H:%M:%SZ").replace(
tzinfo=pytz.utc).astimezone(pytz.timezone("Europe/Madrid")).strftime("%H:%M") tzinfo=pytz.utc).astimezone(pytz.timezone("Europe/Madrid")).strftime("%H:%M")
self.sendReport("🕐 Se ha realizado un marcaje antes a las %s" % time) self.sendReport("🕐 Ya se ha realizado este marcaje antes a las %s" % time)
else: else:
time = datetime.now().astimezone(pytz.timezone("Europe/Madrid")).strftime("%H:%M") time = datetime.now().astimezone(pytz.timezone("Europe/Madrid")).strftime("%H:%M")
conditional_response = { conditional_response = [{
"text": "fichado", "text": "fichado",
"emoji": "🕐🏢🏃‍♂️🏡" "emoji": "🕐🏢🏃‍♂️🏡"
} },{
if typ: "text": "desfichado",
conditional_response = { "emoji": "🏡🏃‍♂️🏢🕐"
"text": "desfichado", }]
"emoji": "🏡🏃‍♂️🏢🕐" self.sendReport('%s Has %s correctamente a las %s' % (conditional_response[int(typ)]['emoji'], conditional_response[int(typ)]['text'], time))
}
self.sendReport('%s Has %s correctamente a las %s' % (conditional_response['emoji'], conditional_response['text'], time))
else: else:
self.sendReport("🟥🕐 No se ha podido fichar, la web no ha devuelto 200 OK") self.sendReport("🟥🕐 No se ha podido fichar, la web no ha devuelto 200 OK")
def calendarCheck(self):
creds = None
configpath = os.path.join(ROOT_DIR, 'googleoauth.json')
if os.path.exists(configpath):
try:
creds = Credentials.from_authorized_user_file(configpath, SCOPES)
except ValueError as error:
print(error)
else:
return False
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(configpath, SCOPES)
creds = flow.run_local_server(port=0)
with open(configpath, 'w') as token:
token.write(creds.to_json())
try:
service = build('calendar', 'v3', credentials=creds)
# Call the Calendar API
now = datetime.utcnow().strftime('%Y-%m-%dT00:00:00Z') # 'Z' indicates UTC time
now_after_one_day = (datetime.utcnow() + relativedelta(days=1)).strftime('%Y-%m-%dT00:00:00Z')
events_result_autoficher = service.events().list(calendarId='e51d3bfb6b352c48afb8bd7a5d494599cc3eaa686800a856796306f50c6d72fd@group.calendar.google.com', timeMin=now,
timeMax=now_after_one_day, maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events_result_holiday = service.events().list(
calendarId='es.spain#holiday@group.v.calendar.google.com',
timeMin=now,
timeMax=now_after_one_day,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events = events_result_autoficher.get('items', []) + events_result_holiday.get('items', [])
if not events:
return False
# Prints the start and name of the next 10 events
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
end = event['end'].get('dateTime', event['end'].get('date'))
if datetime.fromisoformat(start).timestamp() <= datetime.now().timestamp() <= datetime.fromisoformat(end).timestamp():
if event['organizer']['displayName'] == 'autoficher':
return 'Forzado desde autoficher - ' + event['summary']
if 'description' in event and ("Cataluña" in event['description'] or 'Día festivo' in event['description']or 'Celebración\n' in event['description']) and not 'Cambio de horario' in event['summary']:
return event['summary']
except HttpError as error:
print(f'An error occurred: {error}')
return 'Error de llamada a api'
return 'No se que ha pasado :/'
if __name__ == '__main__': if __name__ == '__main__':
af = AutoFicher() af = AutoFicher()

View File

@@ -1,2 +1,5 @@
python-telegram-bot python-telegram-bot
pytz pytz
google-api-python-client
google-auth-httplib2
google-auth-oauthlib