omar d440d5899e fix: avisar por Telegram los dias desactivados por adelantado
Con el horizonte de 35 dias las ausencias se detectan semanas antes y el
aviso del mismo dia no llegaba nunca. Ahora se avisa una vez al dia
siempre que hoy este desactivado.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 12:36:03 +01:00
2021-10-15 14:41:56 +02:00

Autoficher para Timenet

Ficha y desficha automáticamente en Timenet, controlado desde Telegram.

Cómo calcula Timenet (y por qué importa)

Verificado sobre 6 meses de historial (104 días laborables, 376 fichajes):

trabajado = Σ  min(salida, fin_teórico) − max(entrada, inicio_teórico)

Timenet recorta a la ventana teórica. La API guarda dos horas por fichaje: HR (la que fichaste) y H (la que te cuentan). De 376 fichajes, 136 llevaban ajuste: todos salidas, todos recortados hacia atrás. Ninguna entrada ajustada jamás.

Consecuencias, y son todo el diseño de este programa:

  • Sólo importan las entradas. Llegar tarde se debita al minuto; salir tarde no acredita nada. Por eso las entradas se fichan antes de la hora y las salidas después.
  • Acabar el mes en positivo es imposible. 0 días con saldo positivo en 108, y timeExtraHours: false en todos los tipos de jornada. El objetivo es 0 clavado.
  • El calendario laboral (festivos, vacaciones, horarios) lo da cp/calendar. No hay que adivinarlo. Google Calendar queda sólo para ausencias personales.

Servicio (recomendado)

pip install -r requirements.txt          # requiere python-telegram-bot >= 22
cp sample.config.json config.json        # y edítalo
echo "TIMENET_PIN=1234" > .env && chmod 600 .env

cp deploy/autoficher.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now autoficher.service
sudo loginctl enable-linger $USER        # para que sobreviva al logout y al reinicio
systemctl --user status autoficher        # estado
journalctl --user -u autoficher -f        # logs en vivo
systemctl --user restart autoficher       # recargar tras tocar config

Comandos de Telegram

Se registran solos en el menú del chat al arrancar.

comando qué hace
/hoy plan y estado de hoy, con lo que Timenet ha registrado de verdad
/saldo saldo del mes y acumulado
/plan plan de hoy, con botón para re-tirar los dados
/perfil [nombre] ver y cambiar el perfil de jitter
/proximos [n] previsión de los próximos días laborables y sus rangos
/fichar /desfichar fichaje manual inmediato
/off [hoy|dd/mm|dd/mm-dd/mm] no fichar esos días
/on [fecha] volver a fichar ese día
/pausa /reanudar parar o retomar el servicio
/log últimos eventos

Doce minutos antes de cada fichaje llega un aviso con killswitch:

⏳ ENTRADA programada a las 08:59:22 (en 12 min)
[🛑 Cancelar hoy]  [⏭ +15 min]  [✅ Fichar ya]

El bot sólo atiende al chat_id de config.json; cualquier otro chat se ignora.

Perfiles de jitter

Timenet sólo cuenta dentro de la ventana teórica, así que las horas de fichaje sólo pueden variar hacia el lado seguro, y alejarse cuesta presencia real. Más variedad visible = más minutos ahí. Cada perfil elige un punto de ese compromiso. Los cinco dan saldo 0; se cambian en caliente con /perfil.

perfil min/día madrugón entrada 09:00
discreto 2,8 1,3 2 minutos, moda 75 %
natural (por defecto) 4,1 1,8 5 minutos, moda 51 %
camuflado 5,5 2,1 4 minutos, moda 43 %
disperso 7,9 3,0 6 minutos, moda 29 %
sin madrugar 5,7 1,3 2 minutos, moda 77 %
histórico real — — 4 minutos, moda 38 %

min/día es la presencia total de más. madrugón es la parte de esos minutos que te levantas antes por la entrada de la mañana — el resto se lo come la pausa de comer y la salida, que no alargan el día. Por eso existe sin madrugar: entrada de la mañana pegada a las 9 y toda la variedad en la comida y la salida.

Medido sobre los 226 días laborables del año con tools/perfiles.py. Un perfil puede afinar por tramo con sufijo _w<n>: entry_median_w0 gana a entry_median para el tramo de la mañana.

Configuración

Lo esencial va en config.json (ver sample.config.json). geo_jitter_meters desplaza la posición dentro de un disco de ese radio; sin él, las coordenadas salen idénticas al sexto decimal en todos los fichajes.

Herramientas

./venv/bin/python3 tools/tn_inspect.py     # calendario y saldo, contra la API real
./venv/bin/python3 tools/dryrun.py         # simula el año y verifica que no pierde minutos
./venv/bin/python3 tools/test_daemon.py    # 19 pruebas con reloj simulado, sin red
./venv/bin/python3 tools/perfiles.py       # mide cada perfil sobre el calendario real

Modo antiguo (CLI de un disparo)

main.py sigue funcionando para fichajes puntuales desde consola o cron:

python3 main.py -p <pin> [-t <0=Entrada, 1=Salida> | -bt] [-f] [-d]

No aplica la corrección del sesgo ni consulta el calendario de Timenet: pierde de 3 a 5 minutos al día. Úsalo sólo como respaldo manual.

Google Calendar

Para ausencias personales. Descarga el OAuth de la API de Google y déjalo en la raíz como googleoauth_grant.json; el token se guarda en googleoauth.json. Se consulta el calendario autoficher: si un evento cubre el día, no se ficha.

Description
No description provided
Readme 23 MiB
Languages
Python 100%