headway-news-bot

1

Documentação

Headway News Bot

Bot de aprovação do Telegram e monitor de notícias para o canal de carregamento de veículos elétricos @headway74.

O projeto busca notícias sobre carregamento de veículos elétricos, cria rascunhos em russo para o Telegram, anexa mídia relevante quando possível, envia rascunhos ao proprietário para revisão e publica somente após um comando explícito de aprovação.

Princípios Fundamentais

  • Publicação com aprovação humana: rascunho primeiro, publicação no canal somente após публикуй.
  • A voz do canal é independente do provedor e definida em vps/channel_agent_rules.md.
  • O bot escreve para motoristas de VE, compradores de carregadores, proprietários de sites, operadores, instaladores e leitores de infraestrutura de VE.
  • O bot não deve publicar preenchimento de modelo ou mídia enganosa.
  • A mídia do artigo original é preferida. Se não houver mídia original, a busca exata por entidade é preferida. A geração de imagens por IA é permitida somente após um comando explícito do proprietário.

Arquivos Principais

  • vps/monitor.py - monitor de notícias agendado, parsing de fontes, redação por LLM, relatórios sobre a China.
  • vps/bot.py - bot de aprovação do Telegram, comandos de publicar/reescrever/mídia.
  • vps/channel_agent_rules.md - regras editoriais obrigatórias para todo modelo/provedor.
  • vps/database/history.py - histórico SQLite, estatísticas de qualidade, memória de feedback do proprietário.
  • vps/media/precise_image_search.py - busca precisa de imagens para correções de mídia de rascunhos.
  • vps/media/media_status.py - mensagens de status de mídia e orientação ao proprietário.
  • vps/keyboards.py - teclados inline com dados de callback vinculados ao rascunho.
  • vps/media/image_dedup.py - detecção de duplicidade de mídia baseada em ImageHash.
  • vps/backup.py - backups gzip do SQLite.
  • vps/security/rate_limit.py - limitador de taxa de comandos simples em memória.
  • vps/datasette-metadata.json - metadados Datasette somente locais.
  • vps/smoke_check.py - verificação rápida de sanidade do deploy.
  • vps/tests/test_static_guards.py - testes de guarda estáticos para comportamento crítico.

Verificações no VPS

Execute a partir de /opt/headway-news-bot:

./.venv/bin/python vps/smoke_check.py
./.venv/bin/python -m unittest discover -s tests -p "test_*.py"
./.venv/bin/python -m pytest tests
systemctl status headway-news-bot.service --no-pager
systemctl list-timers --all | grep headway

A configuração principal de runtime deve ficar na raiz do projeto .env: /opt/headway-news-bot/.env.

Para compatibilidade, o bot também verifica /opt/headway-news-bot/vps/.env. A ordem de carregamento é a raiz .env primeiro, depois vps/.env para valores ausentes. O bot não falha se qualquer um dos arquivos estiver ausente.

Confiabilidade e gerenciamento do bot

Notificações de erro

O bot captura erros não tratados, escreve-os no log e pode enviar ao proprietário uma notificação curta do Telegram sem tokens ou chaves de API.

Em .env:

ADMIN_TELEGRAM_ID=117574226
ERROR_NOTIFICATIONS_ENABLED=1

Se ADMIN_TELEGRAM_ID não estiver definido, o bot usa TELEGRAM_REVIEW_CHAT_ID.

Rascunhos e botões inline

Abaixo de cada rascunho aparece um painel de ações:

  • ✅ Публиковать - publica apenas o rascunho selecionado.
  • 🖼 Найти фото - inicia a busca precisa de fotos pelas entidades do rascunho.
  • 🎨 Сгенерировать - executa o script de geração existente mediante comando explícito do proprietário.
  • ✏️ Переделать текст - envia o rascunho para reescrita.
  • ❌ Отклонить - remove o rascunho da publicação e registra a decisão no histórico.
  • ℹ️ Почему подходит? - mostra uma explicação curta da fonte, do tema, da mídia e do motivo da seleção.

Os antigos comandos de reply continuam funcionando: публикуй, найди фото ..., сгенерируй, переделай, отклонить, статистика.

Relatório diário

O comando /daily_report mostra um resumo das últimas 24 horas: fontes, notícias encontradas, materiais que passaram no filtro, rascunhos criados, rejeições, erros de fontes, principais fontes e temas do dia.

Se ainda não houver dados, o bot responde: Статистика за сутки пока не накоплена.

Para envio automático diário ao proprietário:

DAILY_REPORT_ENABLED=1
DAILY_REPORT_TIME=09:00
SCHEDULER_ENABLED=1
SCHEDULER_TIMEZONE=Asia/Yekaterinburg

O APScheduler usa IDs de tarefas persistentes e não os duplica na reinicialização.

Pytest

Os smoke-tests raiz estão em tests/ e não exigem token real do Telegram, APIs pagas ou publicação no canal.

python -m pytest tests

Etapa 2: gerenciamento de rascunhos e proteção de mídia

Botões inline de rascunhos

Os teclados estão em vps/keyboards.py. Cada botão contém um draft_id curto, portanto a ação está vinculada a um rascunho específico:

  • publish:{draft_id} - primeiro mostra a confirmação de publicação.
  • confirm_publish:{draft_id} - publica após o segundo clique do proprietário.
  • find_photo:{draft_id} - inicia a busca precisa de fotos.
  • generate_image:{draft_id} - inicia a geração somente mediante comando explícito do proprietário.
  • rewrite:{draft_id} - envia o rascunho para reescrita.
  • reject:{draft_id} - mostra os motivos da rejeição.
  • reject_reason:{draft_id}:{reason} - salva o motivo e remove o rascunho.
  • why:{draft_id} - explica a relevância da notícia.

Os antigos comandos de texto não foram removidos.

ImageHash e proteção contra duplicatas

vps/media/image_dedup.py calcula o perceptual hash da imagem e o armazena em vps/database/image_hashes.db.

Configurações:

IMAGE_HASH_ENABLED=1
IMAGE_HASH_THRESHOLD=10

A busca precisa de fotos via vps/media/precise_image_search.py verifica a imagem encontrada usando ImageHash. Se a foto for semelhante a uma já usada, o bot não a substitui automaticamente e registra isso no log.

Visualização de estatísticas via Datasette

Não é um painel web, mas uma visualização local do SQLite. Não abrir a porta para o exterior.

pip install datasette
datasette vps/database/history.db \
  --host=127.0.0.1 \
  --port=8001 \
  --metadata=vps/datasette-metadata.json

Acesso a partir do computador:

ssh -L 8001:localhost:8001 root@VPS_IP

Depois abrir http://127.0.0.1:8001. Exemplo de unit systemd: deploy/headway-datasette.service.example.

Backup automático do banco

vps/backup.py faz backup gzip de todos os bancos SQLite de vps/database/*.db e armazena os arquivos mais recentes.

No mínimo, entram no backup:

  • history.db
  • image_hashes.db, se o banco já foi criado pela deduplicação ImageHash

Configurações:

BACKUP_ENABLED=1
BACKUP_KEEP_DAYS=14
BACKUP_DIR=backups

Execução manual no Telegram:

/backup_now

O comando está disponível somente para o proprietário. Se o banco ainda não existir, o bot não falha e responde que o backup não foi criado.

Comandos somente do proprietário

Ações críticas verificam o proprietário:

  • publicação;
  • rejeição;
  • geração de imagem;
  • busca de imagem;
  • backup.

Configurações:

OWNER_CHAT_ID=117574226
ADMIN_TELEGRAM_ID=117574226

Se o comando for chamado por quem não é o proprietário, o bot responde: Команда доступна только владельцу.

Rate limit

Limitador simples em memória sem Redis:

RATE_LIMIT_ENABLED=1
RATE_LIMIT_REQUESTS=30
RATE_LIMIT_WINDOW_SECONDS=60
RATE_LIMIT_OWNER_BYPASS=1

Por padrão, o proprietário não está sujeito ao rate limit.

Ao exceder o limite, o bot responde: Слишком много команд. Попробуйте позже.

Verificação pós-deploy

python -m compileall vps
python -m pytest tests -v
./.venv/bin/python vps/smoke_check.py
systemctl restart headway-news-bot.service
systemctl status headway-news-bot.service --no-pager

Modo de treinamento editorial de 7 dias

Este modo temporário é usado quando o canal está sendo ajustado novamente com o feedback do proprietário. Ele não publica automaticamente. Apenas envia rascunhos ao proprietário para revisão.

Objetivo da semana de treinamento:

  • coletar candidatos suficientes todos os dias;
  • manter pelo menos 2 rascunhos fortes publicáveis por dia no fluxo de revisão;
  • salvar comentários do proprietário, rejeições, anotações de mídia e decisões de publicação em history.db;
  • permitir que prompts futuros usem feedback de rejeição recente para que trocas de provedor não mudem a voz do canal.

Arquivos:

  • vps/headway-news-monitor-training7d.timer
  • vps/headway-news-monitor-training7d.service
  • vps/run_training_monitor.sh

Configurações:

TRAINING_DURATION_HOURS=168
TRAINING_LOOKBACK_HOURS=24
TRAINING_TARGET_DRAFTS_PER_DAY=2

Ativar no VPS:

sudo cp vps/headway-news-monitor-training7d.service /etc/systemd/system/
sudo cp vps/headway-news-monitor-training7d.timer /etc/systemd/system/
sudo chmod +x vps/run_training_monitor.sh
sudo systemctl daemon-reload
sudo systemctl enable --now headway-news-monitor-training7d.timer
sudo systemctl start headway-news-monitor-training7d.service

O serviço executa:

/opt/headway-news-bot/.venv/bin/python /opt/headway-news-bot/vps/monitor.py --hours "$TRAINING_LOOKBACK_HOURS" --send-review

Ele não chama o código de publicação no canal. A publicação continua exigindo aprovação do proprietário no Telegram.

Comandos úteis do Telegram durante o treinamento:

  • /training_status - mostra se o modo de treinamento está ativo, quando começou, horas restantes, candidatos, rascunhos, publicações/rejeições do proprietário, reescritas e erros de mídia.
  • /source_quality - mostra a qualidade das fontes por 7 dias por padrão.
  • /source_quality 30 ou /source_quality 90 - mostra uma janela maior de qualidade de fontes.
  • /bad_sources - mostra fontes com baixa aprovação, muitas rejeições, fatos fracos, mídia ruim ou rascunhos fora do tema. O bot apenas recomenda, não desativa fontes automaticamente.
  • /good_topics - mostra temas que chegam à publicação com mais frequência, incluindo infraestrutura de carregamento, carregamento rápido, China, subsídios, hubs, troca de baterias e padrões.
  • /daily_report - inclui o bloco "Treinamento do canal por 7 dias" enquanto o estado de treinamento existir.
  • /memory ou /update_memory - atualiza editorial_memory.md a partir do histórico.

editorial_memory.md mantém anotações manuais fora deste bloco gerado:

<!-- AUTO_HISTORY_START -->
...
<!-- AUTO_HISTORY_END -->

O bloco automático armazena melhores fontes, fontes ruins, bons temas, motivos frequentes de rejeição, regras de mídia e frases a evitar.

Parar manualmente:

sudo systemctl disable --now headway-news-monitor-training7d.timer

O timer se desativa sozinho após a duração configurada.

Segurança

Não commitar .env, chaves de API, tokens do Telegram, chaves SSH, rascunhos de runtime, armazenamento de mídia, logs ou bancos de dados SQLite.

Use .env.example como o único template de ambiente commitado.