feat(vida): ideias do dia com inspiração da internet e "olha o que eu aprendi"

Todo dia, a partir das 9h, o Carinha cria 3 atividades novas (inspiracao.py):
- sinais de fontes abertas: tempo da cidade (Open-Meteo), datas
  comemorativas da semana (lista fixa + feriados da BrasilAPI), "neste dia"
  da Wikipédia e assuntos em alta no Google Trends Brasil;
- o Gemini escolhe ideias leves (descarta tragédia, política e afins) e
  cada uma vira atividade pelo estúdio (Criacao.gerar), com motivo e uma
  curiosidade;
- até 3 tentativas por dia, 30 min entre elas; botão "Criar mais agora";
- as atividades criadas entram na rotina do modo vida (as de hoje com 40%
  de chance) e, com alguém perto, ele conta a curiosidade: legenda e voz
  de robô (ordem "aprendi", até 2x por dia cada);
- painel: cartão "Ideias do dia" no estúdio (Mostrar, Contar, o que ele
  viu na internet, ajustes: quantas, horário, cidade, fontes) e tópico no
  guia.

Correções achadas no caminho:
- WebView de fábrica do S10 (Chrome 102) não tem AbortSignal.timeout: o
  celular nunca mandava o status e a placa o via offline;
- arquivos estáticos com Cache-Control no-cache e o app com LOAD_NO_CACHE:
  o WebView seguia com uma carinha.html velha depois de deploy;
- status do celular recusado vai para o journal (1 linha por minuto);
- legenda mais estreita para não cobrir a bateria.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
julio
2026-10-08 08:13:03 -03:00
co-authored by Claude Opus 5.5
parent 43770c6d67
commit 4777ceb023
19 changed files with 1179 additions and 30 deletions
+9
View File
@@ -132,6 +132,7 @@ class ControlePC:
raise ValueError('Use JSON')
tamanho = int(h.headers.get('Content-Length', '0'))
if tamanho <= 0 or tamanho > 16384:
self.registrar_recusa(rota, 'corpo de %d bytes' % tamanho)
enviar({'erro': 'Corpo vazio ou grande demais'}, 413)
return True
corpo = h.rfile.read(tamanho)
@@ -169,7 +170,15 @@ class ControlePC:
else:
enviar(self.resumo(h.headers.get("Host", "").split(":")[0]))
except (ValueError, TypeError) as e:
self.registrar_recusa(rota, str(e))
enviar({'erro': str(e)}, 400)
except OSError as e:
enviar({'erro': str(e)}, 503)
return True
def registrar_recusa(self, rota, motivo):
"""Pedido do celular recusado: aparece no journal (no máximo 1 linha por minuto)."""
agora = time.monotonic()
if agora - getattr(self, '_recusa_em', -60) >= 60:
self._recusa_em = agora
print('app: recusado', rota, '-', motivo[:160], flush=True)