google calendar
Signed-off-by: Omar Sánchez Pizarro <omar.sanchez@pistacero.net>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
config.json
|
config.json
|
||||||
|
googleoauth.json
|
||||||
13
README.md
13
README.md
@@ -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.
|
||||||
|
|||||||
114
autoficher.py
114
autoficher.py
@@ -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()
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
python-telegram-bot
|
python-telegram-bot
|
||||||
pytz
|
pytz
|
||||||
|
google-api-python-client
|
||||||
|
google-auth-httplib2
|
||||||
|
google-auth-oauthlib
|
||||||
Reference in New Issue
Block a user