Files
wiki/Codigos/notif_ponto.md

20 KiB

title, description, published, date, tags, editor, dateCreated
title description published date tags editor dateCreated
Notificação de Ponto Sistema de notificação ponto true 2026-08-19T21:23:08.082Z markdown 2026-08-15T01:00:06.922Z

Plano do projeto — Bot de lembretes de ponto

1. Objetivo

Construir uma aplicação em Go que, por mensagens no Telegram, ajude aproximadamente dez usuários a não esquecerem a marcação de ponto. Cada usuário possui sua própria credencial na API de ponto. O sistema consulta o histórico de marcações, calcula os próximos eventos relevantes e envia lembretes de:

  • retorno do almoço (entrada2);
  • encerramento do expediente (saida2).

Nesta primeira fase, o Telegram é a única interface. Uma interface web, destinada a um único administrador, será adicionada posteriormente sem duplicar as regras de negócio.

2. Escopo da fase 1

Incluído:

  • bot Telegram para usuários e administrador;
  • solicitação e aprovação de cadastro pelo Telegram;
  • armazenamento em SQLite;
  • credencial de API individual por usuário, armazenada de forma criptografada;
  • autenticação na API de ponto e renovação automática do token;
  • leitura dos últimos oito apontamentos;
  • validação de pontos do dia atual;
  • criação persistente e envio único de lembretes;
  • execução em um container LXC Linux no Proxmox;
  • logs, auditoria e backup do banco.

Fora do escopo inicial:

  • painel web administrativo;
  • regras para feriados municipais, estaduais e nacionais;
  • lembretes de entrada1 e saida1;
  • múltiplos administradores;
  • integrações além de Telegram e API de ponto.

3. Arquitetura

O projeto será um monólito modular em Go. Para o volume esperado, um único processo, um SQLite e um LXC são suficientes, simples de manter e adequados para estudo.

flowchart TD
    T["Usuários e administrador<br/>Telegram"] --> B["Adaptador Telegram"]
    B --> UC["Casos de uso por comando"]

    UC --> U["Usuários e cadastro"]
    UC --> P["Consulta de jornada atual"]
    UC --> R["Motor de regras e cálculos"]
    UC --> Q["Agendador de notificações"]
    UC --> C["Calendário de dias úteis"]

    U --> DB[("SQLite")]
    R --> DB
    Q --> DB
    P --> API["API externa de ponto"]
    Q --> TG["API do Telegram"]

    W["Fase 2: painel web do administrador"] -. reutiliza os mesmos casos de uso .-> UC

Princípios

  • O bot Telegram não contém cálculo de jornada: ele converte cada comando em um caso de uso e envia sua resposta.
  • A API de ponto fica isolada em um módulo próprio.
  • As regras de horário ficam em serviços testáveis, sem dependência de Telegram, SQLite ou HTTP.
  • O SQLite registra estado suficiente para evitar mensagens duplicadas após reinício.
  • Tokens de API ficam somente em memória; senha e token nunca são escritos em logs.

Arquitetura orientada a comandos

Cada comando do Telegram terá um caso de uso explícito. Isso evita que o arquivo do bot vire um conjunto de regras misturadas e permite que o futuro painel web reaprove exatamente a mesma lógica.

flowchart LR
    CMD["/entrada2, /saida2,<br/>/status, /cadastrar"] --> H["Handler Telegram"]
    H --> U["Caso de uso"]
    U --> J["Consulta de jornada"]
    J --> API["API de ponto"]
    U --> R["Regras de cálculo"]
    U --> DB[("SQLite")]
    U --> H
    H --> MSG["Resposta Telegram"]

O agendador é outro consumidor dos mesmos casos de uso. Portanto, o cálculo retornado em /saida2 será exatamente o mesmo usado para criar uma notificação automática de saida2.

3.1 Comandos da fase 1

Comando Quem usa Atividade Resposta esperada
/entrada2 Usuário ativo Consulta o histórico atual e calcula saida1 + 55 min quando houver ao menos dois pontos. Horário previsto de retorno ou explicação do ponto que ainda falta.
/saida2 Usuário ativo Consulta o histórico atual e calcula a saída para completar 7h55 quando houver três pontos. Horário previsto de saída ou explicação do ponto que ainda falta.
/status Usuário ativo Consulta e resume os pontos atuais e os agendamentos do dia. Situação da jornada e próximos lembretes.
/iniciar Qualquer pessoa Identifica o chat_id e apresenta instruções. Boas-vindas ou orientação para cadastro.
/cadastrar Pessoa não cadastrada Cria solicitação de cadastro para o administrador. Confirmação de solicitação pendente.
/aprovar <id> Administrador Ativa um pedido de cadastro. Confirmação e orientação ao novo usuário.
/usuarios Administrador Lista usuários e solicitações pendentes. Resumo administrativo.
/ajuda Todos Mostra somente os comandos permitidos ao perfil atual. Guia de uso.

Regras dos comandos de consulta

/entrada2 e /saida2 fazem uma consulta imediata à API do próprio usuário, independentemente do próximo ciclo do agendador. Eles não enviam lembretes nem mudam um agendamento existente; apenas mostram a projeção atual.

Exemplos de respostas:

/entrada2
Você saiu para almoço às 12:00.
Seu retorno previsto é às 12:55.
/saida2
Você trabalhou 4h00 antes do almoço e retornou às 13:00.
Para completar 7h55, sua saída prevista é às 16:55.

Quando não houver pontos suficientes, a mensagem deve ser clara:

Ainda não consigo calcular sua saída: falta registrar a entrada após o almoço.

3.2 Evolução: comando /registrar

/registrar será uma funcionalidade futura e só poderá efetivamente registrar um ponto se a API externa oferecer um endpoint autorizado de criação de apontamento. A API conhecida até agora apenas autentica e consulta o histórico; ela não confirma a existência dessa operação.

O desenho recomendado é preparar uma abstração sem fingir que o ponto foi registrado:

PointGateway
├── Authenticate(user)
├── GetHistory(user)
└── RegisterPoint(user, instant)  // futura implementação, se a API permitir

Possíveis comportamentos futuros:

  • Se houver endpoint oficial: /registrar pede confirmação, chama a API e mostra o resultado recebido.
  • Se não houver endpoint oficial: o comando não deve dizer que bateu o ponto; pode abrir orientação, registrar uma solicitação manual ou apenas informar que a marcação deve ser feita no sistema oficial.

Por segurança, o fluxo com endpoint oficial deve exigir confirmação explícita, por exemplo /registrar seguido de botão Confirmar registro às 16:55. A auditoria deve gravar intenção, horário, resposta da API e resultado.

4. Integração com a API de ponto

Cada usuário tem e-mail e senha próprios.

  1. Fazer POST de autenticação com e-mail e senha.
  2. Receber client, token e validade do token.
  3. Manter esses valores em memória, associados ao usuário.
  4. Fazer GET do histórico com token, client e e-mail.
  5. A resposta contém os últimos oito apontamentos.
  6. Se o histórico retornar erro de autenticação, autenticar novamente, atualizar client, token e validade e repetir a consulta uma única vez.
  7. Se o novo teste falhar, registrar erro de auditoria e tentar no próximo ciclo do agendador.

As senhas precisam ser criptografadas no SQLite. A chave de criptografia deve ser uma variável de ambiente do LXC, fora do repositório Git.

5. Regras de negócio

5.1 Dia útil

Antes de consultar agendamentos ou a API, verificar se a data é segunda a sexta-feira. Em sábados e domingos, não consultar nem enviar lembretes.

Validação de feriados de Belo Horizonte é uma evolução posterior. Desde o início, ela terá uma interface própria (HolidayProvider) para que possa ser incluída sem alterar o fluxo central:

IsBusinessDay(data, localidade) = segunda a sexta E não é feriado

Na fase 1, não é feriado será considerado verdadeiro; a localidade padrão será Belo Horizonte/MG.

5.2 Pontos válidos

O histórico vem com até oito registros. A aplicação deve:

  1. Converter horários para America/Sao_Paulo.
  2. Manter apenas registros da data atual.
  3. Ordenar por hora crescente.
  4. Considerar, no máximo, quatro pontos do dia.
  5. Interpretar a sequência como:
entrada1 → saida1 → entrada2 → saida2
Pontos no dia Situação
0 Falta entrada1
1 Falta saida1
2 Falta entrada2
3 Falta saida2
4 Jornada concluída

5.3 Lembrete de entrada2

Com dois pontos no dia, considerar que eles são entrada1 e saida1.

horário do lembrete de entrada2 = saida1 + 55 minutos

Exemplo: com saída para almoço às 12:00, o lembrete é agendado para 12:55.

5.4 Lembrete de saida2

Com três pontos no dia, considerar que eles são entrada1, saida1 e entrada2. O objetivo diário é 7h55 de trabalho efetivo.

trabalho_antes_do_almoco = saida1 - entrada1
tempo_restante = 7h55 - trabalho_antes_do_almoco
horario_estimado_saida2 = entrada2 + tempo_restante

O lembrete deve ser agendado para o horário calculado menos uma antecedência configurável. Inicialmente, sugerido: cinco minutos.

Exemplo:

entrada1: 08:00
saida1:   12:00
entrada2: 13:00
trabalho antes do almoço: 4h00
restante: 3h55
saida2 estimada: 16:55
lembrete: 16:50 (antecedência de 5 minutos)

6. Agendamentos e prevenção de duplicidade

Cada lembrete é persistido no SQLite com uma chave única. Uma chave legível pode seguir o formato:

bruno14082026entrada2
bruno14082026saida2

A garantia técnica deve ser a restrição única:

UNIQUE(user_id, reference_date, notification_type)

Isso evita colisões se nome de usuário mudar ou dois nomes forem semelhantes.

Estados

Status Significado
scheduled Agendamento criado, ainda não é hora de enviar
ready_to_send O horário chegou; execute_send = true
sent Mensagem enviada com sucesso
cancelled Ponto foi detectado antes do lembrete ou o dia deixou de ser aplicável
failed Houve falha de envio; permite retentativa controlada

Pela regra aprovada, quando houver um agendamento pendente com execute_send = true, o sistema envia a mensagem sem nova consulta à API. Caso não exista agendamento ou ele esteja com execute_send = false, o sistema consulta o histórico para decidir se cria/cancela agendamentos.

Evolução recomendada: consultar a API uma última vez antes do envio. Isso elimina um falso lembrete quando a pessoa registra o ponto entre a última consulta e o horário agendado.

7. Fluxograma operacional

flowchart TD
    I["Ciclo do agendador, por usuário"] --> D{"É dia útil?<br/>Segunda a sexta"}
    D -->|Não| X["Encerrar ciclo"]
    D -->|Sim| A{"Existe agendamento vencido<br/>com execute_send = true?"}

    A -->|Sim| T["Enviar mensagem Telegram"]
    T --> M["Registrar status sent e sent_at"]
    M --> X

    A -->|Não| H["Consultar histórico de pontos"]
    H --> E{"Erro de autenticação?"}
    E -->|Sim| L["Autenticar novamente"]
    L --> R["Atualizar client, token e validade"]
    R --> H
    E -->|Não| V["Filtrar pontos de hoje e ordenar"]

    V --> P{"Quantidade de pontos"}
    P -->|0 ou 1| X
    P -->|2| EN["Criar, se inexistente:<br/>entrada2 = saida1 + 55 min"]
    P -->|3| SA["Calcular saída2 e criar,<br/>se inexistente, o agendamento"]
    P -->|4| F["Jornada completa;<br/>não criar lembretes"]
    EN --> X
    SA --> X
    F --> X

8. Estrutura de pastas e arquivos

bot-pontos/
├── cmd/
│   └── bot/
│       └── main.go
├── internal/
│   ├── app/
│   │   └── application.go
│   ├── config/
│   │   └── config.go
│   ├── domain/
│   │   ├── user.go
│   │   ├── workday.go
│   │   ├── notification.go
│   │   └── point.go
│   ├── usecase/
│   │   ├── get_entrada2.go
│   │   ├── get_saida2.go
│   │   ├── get_status.go
│   │   ├── register_user.go
│   │   ├── approve_user.go
│   │   └── register_point.go      # contrato futuro
│   ├── pointsapi/
│   │   ├── client.go
│   │   ├── auth.go
│   │   └── models.go
│   ├── rules/
│   │   ├── point_validator.go
│   │   ├── schedule_calculator.go
│   │   └── business_day.go
│   ├── scheduler/
│   │   └── scheduler.go
│   ├── telegram/
│   │   ├── bot.go
│   │   ├── command_router.go
│   │   ├── entrada2_handler.go
│   │   ├── saida2_handler.go
│   │   ├── status_handler.go
│   │   ├── admin_handler.go
│   │   └── messages.go
│   ├── repository/
│   │   ├── sqlite.go
│   │   ├── users.go
│   │   ├── notifications.go
│   │   └── audit.go
│   ├── security/
│   │   └── credentials.go
│   └── audit/
│       └── logger.go
├── migrations/
│   ├── 001_initial_schema.sql
│   └── 002_indexes.sql
├── configs/
│   └── config.example.yaml
├── deploy/
│   ├── bot-pontos.service
│   └── backup-sqlite.sh
├── tests/
│   ├── rules_test.go
│   └── scheduler_test.go
├── .env.example
├── .gitignore
├── go.mod
├── go.sum
├── README.md
└── PLANO_PROJETO_BOT_PONTOS.md

Responsabilidade de cada arquivo

Arquivo/pasta Responsabilidade
cmd/bot/main.go Inicializa configuração, banco, bot, agendador e encerra o processo com segurança.
internal/app/application.go Conecta dependências e descreve os casos de uso principais.
internal/config/config.go Lê variáveis de ambiente e parâmetros: Telegram, banco, criptografia, fuso e intervalo do ciclo.
internal/domain/*.go Entidades puras: usuário, ponto, jornada e notificação; sem HTTP ou SQLite.
internal/usecase/get_entrada2.go Orquestra a consulta imediata e retorna o horário previsto de entrada2.
internal/usecase/get_saida2.go Orquestra a consulta imediata e retorna o cálculo de saida2 para completar 7h55.
internal/usecase/get_status.go Resume os pontos atuais e os agendamentos do usuário.
internal/usecase/register_user.go Cria e consulta solicitações de cadastro.
internal/usecase/approve_user.go Aprova cadastro e ativa o usuário.
internal/usecase/register_point.go Reserva o contrato do futuro /registrar; não registra nada enquanto a API não oferecer suporte oficial.
internal/pointsapi/client.go Requisições HTTP à API; consulta o histórico.
internal/pointsapi/auth.go Login, cache em memória e renovação de token por usuário.
internal/pointsapi/models.go Estruturas de request/response da API externa e conversão para o domínio.
internal/rules/point_validator.go Filtra pontos de hoje, ordena e determina se há 0, 1, 2, 3 ou 4 registros.
internal/rules/schedule_calculator.go Calcula saida1 + 55 min e a saida2 baseada em 7h55.
internal/rules/business_day.go Verifica segunda a sexta; depois receberá o provedor de feriados de Belo Horizonte.
internal/scheduler/scheduler.go Executa o ciclo periódico: envia vencidos ou consulta a API/cria agendamentos.
internal/telegram/bot.go Mantém conexão e atualização do bot Telegram.
internal/telegram/command_router.go Autoriza o perfil e encaminha cada comando ao respectivo handler.
internal/telegram/*_handler.go Converte o comando Telegram em chamada de caso de uso e formata a resposta.
internal/telegram/messages.go Centraliza textos das notificações e respostas do bot.
internal/repository/sqlite.go Abre SQLite, aplica migrations e configura modo WAL.
internal/repository/users.go CRUD de usuários, pedidos de cadastro e permissões.
internal/repository/notifications.go Cria, busca, atualiza e deduplica agendamentos.
internal/repository/audit.go Persiste erros, ações administrativas e eventos relevantes.
internal/security/credentials.go Criptografa/descriptografa credenciais individuais dos usuários.
migrations/ Evolução versionada do esquema SQLite.
deploy/bot-pontos.service Serviço systemd para manter o binário ativo no LXC.
deploy/backup-sqlite.sh Backup consistente diário do banco SQLite.
tests/ Testes unitários para cálculos e para o fluxo de agendamento.

9. Modelo inicial de banco

users
- id
- telegram_chat_id (único)
- display_name
- api_email
- encrypted_api_password
- role (user | admin)
- status (pending | active | disabled)
- created_at
- updated_at

work_rules
- id
- user_id (único)
- lunch_reminder_after_minutes (padrão 55)
- daily_work_minutes (padrão 475 = 7h55)
- exit_reminder_lead_minutes (padrão 5)
- timezone (America/Sao_Paulo)
- active

scheduled_notifications
- id
- notification_key
- user_id
- reference_date
- notification_type (entrada2 | saida2)
- scheduled_at
- execute_send
- status
- sent_at
- telegram_message_id
- created_at
- updated_at

audit_logs
- id
- user_id (opcional)
- event_type
- details
- created_at

10. Ambiente LXC no Proxmox

Recomendação inicial:

  • Debian 12;
  • 1 vCPU;
  • 1 GB de RAM;
  • 8 a 16 GB de disco;
  • fuso horário America/Sao_Paulo;
  • saída HTTPS para a API de ponto e para o Telegram;
  • serviço Go controlado por systemd;
  • banco em /var/lib/bot-pontos/app.db;
  • backup diário do SQLite e backup periódico do próprio LXC pelo Proxmox.

Em produção, o LXC precisa apenas do binário Go compilado, certificados CA, dados de fuso horário e SQLite. O compilador Go pode ficar apenas no ambiente de desenvolvimento ou CI.

11. Sequência de implementação para estudo

  1. Criar repositório, estrutura de pastas e projeto Go (go mod init).
  2. Implementar configuração, logs e SQLite com migrations.
  3. Criar entidades de domínio e testes dos cálculos de 55 minutos e 7h55.
  4. Implementar o cliente da API com mocks e teste de reautenticação.
  5. Implementar persistência de usuários e agendamentos com a chave única.
  6. Construir o ciclo do agendador usando dados simulados.
  7. Integrar o Telegram: cadastro, aprovação e mensagens.
  8. Fazer testes de ponta a ponta com um único usuário de teste.
  9. Criar LXC, instalar o serviço e configurar backup.
  10. Cadastrar os demais usuários gradualmente.

12. Casos de teste mínimos

  • Sábado ou domingo: nenhuma consulta e nenhuma notificação.
  • Dois pontos: cria apenas um agendamento de entrada2 para saida1 + 55 min.
  • Três pontos: cria apenas um agendamento de saida2 para completar 7h55.
  • Quatro pontos: não cria lembrete.
  • Mesmo ciclo rodado duas vezes: não duplica a mesma chave de agendamento.
  • Reinício da aplicação: agendamento persiste e é enviado uma vez.
  • Token expirado: autentica novamente e repete a consulta.
  • Falha contínua na API ou Telegram: registra auditoria sem travar os demais usuários.

13. Evoluções posteriores

  • Feriados de Belo Horizonte/MG e feriados nacionais por um HolidayProvider (API ou calendário local com atualização controlada).
  • Painel web para o administrador com autenticação.
  • Tela de usuários, regras, agendamentos, auditoria e erros.
  • Revalidação da API antes de cada envio para eliminar lembretes obsoletos.
  • Painel de saúde da aplicação e alertas para falhas repetidas.
  • Migração para PostgreSQL apenas se o número de usuários, concorrência ou necessidade de relatórios crescer substancialmente.

14. Resultado esperado

O projeto é viável para uso diário: o volume é baixo, as regras são objetivas e SQLite atende bem. Também é um bom projeto de estudo porque exercita Go, HTTP, autenticação, persistência, criptografia, agendamento, testes, operações Linux e integração com Telegram, sem introduzir complexidade de microserviços cedo demais.