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:
+35
-19
@@ -283,6 +283,11 @@ class Criacao:
|
||||
with self.db() as db:
|
||||
db.execute('CREATE TABLE IF NOT EXISTS criacoes(id INTEGER PRIMARY KEY, horario REAL, tipo TEXT, nome TEXT, '
|
||||
'emoji TEXT, receita TEXT, ia TEXT, pedido TEXT, excluida INTEGER DEFAULT 0)')
|
||||
# origem: 'pedido' (alguém pediu no estúdio) ou 'dia' (ideia do dia); motivo e aprendi vêm da ideia do dia
|
||||
colunas = {c[1] for c in db.execute('PRAGMA table_info(criacoes)')}
|
||||
for coluna, tipo in (('origem', "TEXT DEFAULT 'pedido'"), ('motivo', "TEXT DEFAULT ''"), ('aprendi', "TEXT DEFAULT ''")):
|
||||
if coluna not in colunas:
|
||||
db.execute(f'ALTER TABLE criacoes ADD COLUMN {coluna} {tipo}')
|
||||
|
||||
@contextmanager
|
||||
def db(self):
|
||||
@@ -368,24 +373,29 @@ class Criacao:
|
||||
self.avisar()
|
||||
return self.status()
|
||||
|
||||
def gerar(self, tipo, pedido, ia, base=None, origem='pedido', motivo='', aprendi=''):
|
||||
"""Pede a receita à IA, valida e guarda (síncrono). Devolve o id da criação."""
|
||||
schema = dict(expressao=SCHEMA_EXPRESSAO, atividade=SCHEMA_ATIVIDADE, figurinha=SCHEMA_FIGURINHA)[tipo]
|
||||
bruto = self.chamar(ia, montar_prompt(tipo, pedido, base), schema)
|
||||
if tipo == 'expressao':
|
||||
receita = validar_expressao(bruto)
|
||||
elif tipo == 'atividade':
|
||||
receita = validar_atividade(bruto)
|
||||
else:
|
||||
emoji = _emoji(bruto.pop('emoji', '')) if isinstance(bruto, dict) else '🎨'
|
||||
desenho = validar_desenho(bruto)
|
||||
receita = dict(nome=desenho['nome'][:30], emoji=emoji, desenho=desenho)
|
||||
with self.lock, self.db() as db:
|
||||
ident = db.execute('INSERT INTO criacoes(horario, tipo, nome, emoji, receita, ia, pedido, origem, motivo, aprendi) '
|
||||
'VALUES (?,?,?,?,?,?,?,?,?,?)',
|
||||
(time.time(), tipo, receita['nome'], receita['emoji'], json.dumps(receita, ensure_ascii=False),
|
||||
ia, pedido, origem, motivo, aprendi)).lastrowid
|
||||
db.execute('DELETE FROM criacoes WHERE id NOT IN (SELECT id FROM criacoes ORDER BY id DESC LIMIT ?)', (MAX_CRIACOES,))
|
||||
return ident
|
||||
|
||||
def _criar(self, tipo, pedido, ia, base):
|
||||
try:
|
||||
schema = dict(expressao=SCHEMA_EXPRESSAO, atividade=SCHEMA_ATIVIDADE, figurinha=SCHEMA_FIGURINHA)[tipo]
|
||||
bruto = self.chamar(ia, montar_prompt(tipo, pedido, base), schema)
|
||||
if tipo == 'expressao':
|
||||
receita = validar_expressao(bruto)
|
||||
elif tipo == 'atividade':
|
||||
receita = validar_atividade(bruto)
|
||||
else:
|
||||
emoji = _emoji(bruto.pop('emoji', '')) if isinstance(bruto, dict) else '🎨'
|
||||
desenho = validar_desenho(bruto)
|
||||
receita = dict(nome=desenho['nome'][:30], emoji=emoji, desenho=desenho)
|
||||
with self.lock, self.db() as db:
|
||||
ident = db.execute('INSERT INTO criacoes(horario, tipo, nome, emoji, receita, ia, pedido) VALUES (?,?,?,?,?,?,?)',
|
||||
(time.time(), tipo, receita['nome'], receita['emoji'],
|
||||
json.dumps(receita, ensure_ascii=False), ia, pedido)).lastrowid
|
||||
db.execute('DELETE FROM criacoes WHERE id NOT IN (SELECT id FROM criacoes ORDER BY id DESC LIMIT ?)', (MAX_CRIACOES,))
|
||||
self.tocar(ident) # mostra na hora no celular
|
||||
self.tocar(self.gerar(tipo, pedido, ia, base)) # mostra na hora no celular
|
||||
except Exception as e:
|
||||
with self.lock:
|
||||
self.erro = str(e)[:200]
|
||||
@@ -397,12 +407,18 @@ class Criacao:
|
||||
# ------------------------------------------------------------ biblioteca
|
||||
def item(self, ident):
|
||||
with self.db() as db:
|
||||
row = db.execute('SELECT id, horario, tipo, nome, emoji, receita, ia, pedido FROM criacoes WHERE id=? AND excluida=0',
|
||||
(ident,)).fetchone()
|
||||
row = db.execute('SELECT id, horario, tipo, nome, emoji, receita, ia, pedido, origem, motivo, aprendi FROM criacoes '
|
||||
'WHERE id=? AND excluida=0', (ident,)).fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return dict(id=row[0], horario=row[1], tipo=row[2], nome=row[3], emoji=row[4], receita=json.loads(row[5]),
|
||||
ia=row[6], pedido=row[7])
|
||||
ia=row[6], pedido=row[7], origem=row[8] or 'pedido', motivo=row[9] or '', aprendi=row[10] or '')
|
||||
|
||||
def atividades(self):
|
||||
"""Atividades criadas (do estúdio e do dia): o modo vida sorteia entre elas."""
|
||||
with self.db() as db:
|
||||
rows = db.execute("SELECT id FROM criacoes WHERE excluida=0 AND tipo='atividade' ORDER BY id DESC LIMIT 60").fetchall()
|
||||
return [i for i in (self.item(r[0]) for r in rows) if i]
|
||||
|
||||
def lista(self):
|
||||
with self.db() as db:
|
||||
|
||||
Reference in New Issue
Block a user