El 96% de las entradas caian en solo dos minutos (08:58 / 08:59). El historial
real del usuario tiene 4 valores repartidos, o sea que el bot resultaba MAS
regular que una persona.
Tres cosas estaban mal:
- La deriva de la costumbre se aplicaba SUMANDO segundos. Cuando salia negativa
hundia el valor contra el suelo y amontonaba medio año en el mismo minuto,
justo lo contrario de lo que pretendia. Ahora va en escala logaritmica:
multiplica el centro en vez de sumarse. Solo con eso la moda baja del 50% al
39%, sin coste ninguno.
- La distribucion diaria era uniforme. Una lognormal da mas minutos distintos
por menos presencia extra, porque la mayoria de los dias caen baratos cerca de
la hora y solo unos pocos se van lejos. Ademas es la forma real de la llegada
de alguien que intenta ser puntual.
- El tope recortaba con min(), asi que un 3% de los dias caia exactamente en el
limite y se veia como un muro en el histograma. Ahora resortea.
El compromiso de fondo no tiene truco: solo se puede variar hacia atras, y
alejarse cuesta presencia. Perfiles medidos sobre los 226 dias laborables del
año, los tres con saldo 0:
discreto 2,8 min/dia entrada 2 minutos, moda 75%
natural 4,2 min/dia entrada 5 minutos, moda 51% <- por defecto
camuflado 5,5 min/dia entrada 4 minutos, moda 43%
Ademas, /proximos mostraba una banda estimada que ocultaba la cola. Ahora se
planifican 21 dias por adelantado y se muestran las horas exactas, con lo que
la prevision es real y se puede decidir sobre ella. El plan guardado se
revalida contra el calendario de Timenet y se regenera si cambia el tipo de
jornada de un dia. Google Calendar se consulta con una sola autenticacion para
todo el horizonte.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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: falseen 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 |
/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.
Configuración
Lo esencial va en config.json (ver sample.config.json). Los valores por defecto
del jitter están en app/settings.py:
| perfil | entry_before / exit_after (s) |
presencia extra | colchón mínimo |
|---|---|---|---|
| holgado | 40-150 / 30-130 | 5,67 min/día | 30 s |
| equilibrado (por defecto) | 30-100 / 20-70 | 3,55 min/día | 20 s |
| ajustado | 20-70 / 15-45 | 2,70 min/día | 15 s |
Los tres dan saldo 0; cambia cuánta presencia real cuestan y cuánto retraso del daemon aguantan antes de perder un minuto.
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
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.