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) ---------------------- ```bash 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 ``` ```bash 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 | | `/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 ------------ ```bash ./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 [-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.