akra35567 commited on
Commit
cb0b72e
·
verified ·
1 Parent(s): 40edf39

Update modules/database.py

Browse files
Files changed (1) hide show
  1. modules/database.py +314 -38
modules/database.py CHANGED
@@ -1,4 +1,4 @@
1
- # modules/database.py — AKIRA V21 ULTIMATE (Dezembro 2025) - COMPLETO
2
  """
3
  Banco de dados SQLite para Akira IA - COMPLETAMENTE ATUALIZADO
4
  ✅ Sistema de usuários privilegiados completo
@@ -7,7 +7,9 @@ Banco de dados SQLite para Akira IA - COMPLETAMENTE ATUALIZADO
7
  ✅ Transições graduais de humor (3 níveis)
8
  ✅ Memória emocional BERT GoEmotions
9
  ✅ Backup e otimização automática
10
- MÉTODOS FALTANTES ADICIONADOS: salvar_training_example, registrar_tom_usuario, salvar_aprendizado_detalhado (com numero_usuario)
 
 
11
  """
12
  import sqlite3
13
  import time
@@ -35,7 +37,7 @@ class Database:
35
  self._ensure_all_columns_and_indexes()
36
  self._sincronizar_usuarios_privilegiados() # Sincroniza com config
37
 
38
- logger.info(f"✅ Banco V21 inicializado: {self.db_path}")
39
 
40
  # ================================================================
41
  # ISOLAMENTO CRIPTOGRÁFICO (V21 MELHORADO)
@@ -43,7 +45,7 @@ class Database:
43
 
44
  def _gerar_contexto_id(self, numero: str, tipo: str = 'auto') -> str:
45
  if tipo == 'auto':
46
- if "@g.us" in str(numero) or "120363" in str(numero):
47
  tipo = "grupo"
48
  else:
49
  tipo = "pv"
@@ -117,17 +119,48 @@ class Database:
117
  with self._get_connection() as conn:
118
  c = conn.cursor()
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  indexes = [
121
  "CREATE INDEX IF NOT EXISTS idx_mensagens_numero ON mensagens(numero, deletado)",
122
  "CREATE INDEX IF NOT EXISTS idx_mensagens_contexto ON mensagens(contexto_id)",
123
  "CREATE INDEX IF NOT EXISTS idx_mensagens_timestamp ON mensagens(timestamp)",
 
 
124
  "CREATE INDEX IF NOT EXISTS idx_contexto_numero ON contexto(numero)",
 
125
  "CREATE INDEX IF NOT EXISTS idx_usuarios_numero ON usuarios_privilegiados(numero)",
126
  "CREATE INDEX IF NOT EXISTS idx_transicoes_numero ON transicoes_humor(numero, timestamp)",
127
  "CREATE INDEX IF NOT EXISTS idx_girias_numero ON girias_aprendidas(numero, giria)",
128
  "CREATE INDEX IF NOT EXISTS idx_training_qualidade ON training_examples(qualidade_score, usado)",
129
  "CREATE INDEX IF NOT EXISTS idx_comandos_numero ON comandos_executados(numero, timestamp)",
130
- "CREATE INDEX IF NOT EXISTS idx_reset_numero ON reset_log(numero, timestamp)"
 
 
131
  ]
132
 
133
  for idx_query in indexes:
@@ -143,7 +176,7 @@ class Database:
143
  logger.error(f"Erro ao verificar colunas/índices: {e}")
144
 
145
  # ================================================================
146
- # INICIALIZAÇÃO COMPLETA
147
  # ================================================================
148
 
149
  def _init_db(self):
@@ -151,23 +184,28 @@ class Database:
151
  with self._get_connection() as conn:
152
  c = conn.cursor()
153
 
154
- # TABELA PRINCIPAL DE MENSAGENS
155
  c.execute('''
156
  CREATE TABLE IF NOT EXISTS mensagens (
157
  id INTEGER PRIMARY KEY AUTOINCREMENT,
158
  usuario TEXT NOT NULL,
 
159
  mensagem TEXT NOT NULL,
160
  resposta TEXT NOT NULL,
161
  numero TEXT NOT NULL,
162
  contexto_id TEXT NOT NULL,
163
  tipo_contexto TEXT DEFAULT 'pv',
 
164
  is_reply BOOLEAN DEFAULT 0,
165
  mensagem_original TEXT,
166
  reply_to_bot BOOLEAN DEFAULT 0,
 
167
  humor TEXT DEFAULT 'normal_ironico',
168
  modo_resposta TEXT DEFAULT 'normal_ironico',
169
  emocao_detectada TEXT,
170
  confianca_emocao REAL DEFAULT 0.5,
 
 
171
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
172
  deletado BOOLEAN DEFAULT 0
173
  )
@@ -193,7 +231,7 @@ class Database:
193
  )
194
  ''')
195
 
196
- # TABELA DE CONTEXTO
197
  c.execute('''
198
  CREATE TABLE IF NOT EXISTS contexto (
199
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -210,6 +248,26 @@ class Database:
210
  tom TEXT DEFAULT 'normal',
211
  emocao_tendencia TEXT DEFAULT 'neutral',
212
  volatilidade REAL DEFAULT 0.5,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  data_criacao DATETIME DEFAULT CURRENT_TIMESTAMP,
214
  data_atualizacao DATETIME DEFAULT CURRENT_TIMESTAMP
215
  )
@@ -262,6 +320,21 @@ class Database:
262
  )
263
  ''')
264
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  # TABELA DE COMANDOS EXECUTADOS
266
  c.execute('''
267
  CREATE TABLE IF NOT EXISTS comandos_executados (
@@ -290,7 +363,7 @@ class Database:
290
 
291
  conn.commit()
292
 
293
- logger.info("✅ Todas as tabelas criadas/verificadas")
294
 
295
  except Exception as e:
296
  logger.error(f"❌ Erro ao criar tabelas: {e}")
@@ -526,7 +599,8 @@ class Database:
526
  tabelas_para_resetar = [
527
  "mensagens", "contexto", "transicoes_humor",
528
  "girias_aprendidas", "training_examples",
529
- "comandos_executados", "reset_log"
 
530
  ]
531
 
532
  for tabela in tabelas_para_resetar:
@@ -549,7 +623,7 @@ class Database:
549
  }
550
 
551
  # ================================================================
552
- # MÉTODOS DE RECUPERAÇÃO (COM ISOLAMENTO)
553
  # ================================================================
554
 
555
  def recuperar_mensagens(self, numero: str, limite: int = 10) -> List[Tuple]:
@@ -557,7 +631,8 @@ class Database:
557
  results = self._execute_with_retry(
558
  """
559
  SELECT mensagem, resposta, is_reply, mensagem_original, reply_to_bot,
560
- humor, modo_resposta, emocao_detectada, confianca_emocao
 
561
  FROM mensagens
562
  WHERE numero = ? AND deletado = 0
563
  ORDER BY timestamp DESC
@@ -568,13 +643,18 @@ class Database:
568
  )
569
 
570
  if results:
571
- return [
572
- (
 
 
 
 
 
 
573
  r[0], r[1], bool(r[2]), r[3], bool(r[4]),
574
- r[5], r[6], r[7], r[8]
575
- )
576
- for r in results[::-1]
577
- ]
578
  return []
579
 
580
  except Exception as e:
@@ -609,7 +689,7 @@ class Database:
609
  """
610
  SELECT contexto_id, tipo_contexto, historico, humor_atual,
611
  modo_resposta, nivel_transicao, humor_alvo, termos,
612
- girias, tom, emocao_tendencia, volatilidade
613
  FROM contexto
614
  WHERE numero = ?
615
  """,
@@ -631,7 +711,8 @@ class Database:
631
  "girias": row[8],
632
  "tom": row[9] or "normal",
633
  "emocao_tendencia": row[10] or "neutral",
634
- "volatilidade": row[11] or 0.5
 
635
  }
636
 
637
  return {
@@ -646,7 +727,8 @@ class Database:
646
  "girias": "",
647
  "tom": "normal",
648
  "emocao_tendencia": "neutral",
649
- "volatilidade": 0.5
 
650
  }
651
 
652
  except Exception as e:
@@ -663,7 +745,8 @@ class Database:
663
  "girias": "",
664
  "tom": "normal",
665
  "emocao_tendencia": "neutral",
666
- "volatilidade": 0.5
 
667
  }
668
 
669
  def recuperar_historico_humor(self, numero: str, limite: int = 5) -> List[Dict]:
@@ -768,7 +851,7 @@ class Database:
768
  logger.error(f"Erro ao marcar exemplos: {e}")
769
 
770
  # ================================================================
771
- # MÉTODOS DE SALVAMENTO (COM CONTEXTO)
772
  # ================================================================
773
 
774
  def salvar_mensagem(self, usuario: str, mensagem: str, resposta: str,
@@ -777,42 +860,102 @@ class Database:
777
  humor: str = 'normal_ironico',
778
  modo_resposta: str = 'normal_ironico',
779
  emocao_detectada: str = None,
780
- confianca_emocao: float = 0.5):
 
 
 
 
 
 
781
  try:
782
  numero_final = str(numero or usuario).strip()
783
  contexto_id = self._gerar_contexto_id(numero_final, 'auto')
784
- tipo_contexto = "grupo" if ("@g.us" in numero_final or "120363" in numero_final) else "pv"
 
 
 
 
 
 
 
785
 
786
  self._execute_with_retry(
787
  """
788
  INSERT INTO mensagens
789
- (usuario, mensagem, resposta, numero, contexto_id, tipo_contexto,
790
- is_reply, mensagem_original, reply_to_bot, humor, modo_resposta,
791
- emocao_detectada, confianca_emocao)
792
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
793
  """,
794
  (
795
- usuario[:50], mensagem[:1000], resposta[:1000], numero_final,
796
- contexto_id, tipo_contexto, int(is_reply), mensagem_original,
797
- int(reply_to_bot), humor, modo_resposta,
798
- emocao_detectada, confianca_emocao
 
 
799
  ),
800
  commit=True,
801
  fetch=False
802
  )
803
 
804
- logger.debug(f"✅ Mensagem salva: {numero_final} ({tipo_contexto})")
805
  return True
806
 
807
  except Exception as e:
808
  logger.error(f"Erro ao salvar mensagem: {e}")
809
  return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
810
 
811
  def salvar_contexto(self, numero: str, **kwargs):
812
  try:
813
  numero_final = str(numero).strip()
814
  contexto_id = self._gerar_contexto_id(numero_final, 'auto')
815
  tipo_contexto = "grupo" if ("@g.us" in numero_final or "120363" in numero_final) else "pv"
 
 
 
 
816
 
817
  campos = {
818
  "numero": numero_final,
@@ -941,6 +1084,63 @@ class Database:
941
  except Exception as e:
942
  logger.error(f"Erro ao salvar transição: {e}")
943
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
944
  # ================================================================
945
  # MÉTODOS FALTANTES ADICIONADOS (RESOLVEM OS ERROS)
946
  # ================================================================
@@ -975,12 +1175,12 @@ class Database:
975
  self._execute_with_retry(
976
  """
977
  INSERT INTO mensagens
978
- (usuario, mensagem, resposta, numero, contexto_id, tipo_contexto,
979
  humor, modo_resposta, emocao_detectada, confianca_emocao)
980
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
981
  """,
982
  (
983
- "SISTEMA_TOM",
984
  f"[TOM_DETECTADO: {tom}] {mensagem_contexto or ''}",
985
  "",
986
  numero_final,
@@ -997,7 +1197,8 @@ class Database:
997
  self.salvar_contexto(
998
  numero=numero_final,
999
  tom=tom,
1000
- emocao_tendencia=tom
 
1001
  )
1002
  logger.info(f"✅ Tom registrado: {tom} (confiança: {confianca:.2f}) para {numero_final}")
1003
  return True
@@ -1041,6 +1242,81 @@ class Database:
1041
  logger.error(f"❌ Erro ao salvar aprendizado detalhado: {e}")
1042
  return False
1043
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1044
  # ================================================================
1045
  # FECHAMENTO SEGURO DA CONEXÃO
1046
  # ================================================================
 
1
+ # modules/database.py — AKIRA V21 ULTIMATE (Dezembro 2025) - COMPLETO E ADAPTADO
2
  """
3
  Banco de dados SQLite para Akira IA - COMPLETAMENTE ATUALIZADO
4
  ✅ Sistema de usuários privilegiados completo
 
7
  ✅ Transições graduais de humor (3 níveis)
8
  ✅ Memória emocional BERT GoEmotions
9
  ✅ Backup e otimização automática
10
+ ADAPTADO ao payload do index.js
11
+ ✅ NOVAS TABELAS: audio_transcricoes, grupo_contexto
12
+ ✅ NOVAS COLUNAS: tipo_mensagem, reply_info_json
13
  """
14
  import sqlite3
15
  import time
 
37
  self._ensure_all_columns_and_indexes()
38
  self._sincronizar_usuarios_privilegiados() # Sincroniza com config
39
 
40
+ logger.info(f"✅ Banco V21 ADAPTADO inicializado: {self.db_path}")
41
 
42
  # ================================================================
43
  # ISOLAMENTO CRIPTOGRÁFICO (V21 MELHORADO)
 
45
 
46
  def _gerar_contexto_id(self, numero: str, tipo: str = 'auto') -> str:
47
  if tipo == 'auto':
48
+ if "@g.us" in str(numero) or "120363" in str(numero) or "grupo_" in str(numero):
49
  tipo = "grupo"
50
  else:
51
  tipo = "pv"
 
119
  with self._get_connection() as conn:
120
  c = conn.cursor()
121
 
122
+ # NOVAS COLUNAS PARA TABELA MENSAGENS
123
+ try:
124
+ c.execute("ALTER TABLE mensagens ADD COLUMN tipo_mensagem TEXT DEFAULT 'texto'")
125
+ except sqlite3.OperationalError:
126
+ pass # Coluna já existe
127
+
128
+ try:
129
+ c.execute("ALTER TABLE mensagens ADD COLUMN reply_info_json TEXT")
130
+ except sqlite3.OperationalError:
131
+ pass # Coluna já existe
132
+
133
+ try:
134
+ c.execute("ALTER TABLE mensagens ADD COLUMN usuario_nome TEXT DEFAULT ''")
135
+ except sqlite3.OperationalError:
136
+ pass # Coluna já existe
137
+
138
+ try:
139
+ c.execute("ALTER TABLE mensagens ADD COLUMN grupo_id TEXT DEFAULT ''")
140
+ except sqlite3.OperationalError:
141
+ pass # Coluna já existe
142
+
143
+ try:
144
+ c.execute("ALTER TABLE mensagens ADD COLUMN grupo_nome TEXT DEFAULT ''")
145
+ except sqlite3.OperationalError:
146
+ pass # Coluna já existe
147
+
148
  indexes = [
149
  "CREATE INDEX IF NOT EXISTS idx_mensagens_numero ON mensagens(numero, deletado)",
150
  "CREATE INDEX IF NOT EXISTS idx_mensagens_contexto ON mensagens(contexto_id)",
151
  "CREATE INDEX IF NOT EXISTS idx_mensagens_timestamp ON mensagens(timestamp)",
152
+ "CREATE INDEX IF NOT EXISTS idx_mensagens_tipo ON mensagens(tipo_mensagem)",
153
+ "CREATE INDEX IF NOT EXISTS idx_mensagens_grupo ON mensagens(grupo_id, deletado)",
154
  "CREATE INDEX IF NOT EXISTS idx_contexto_numero ON contexto(numero)",
155
+ "CREATE INDEX IF NOT EXISTS idx_contexto_tipo ON contexto(tipo_contexto)",
156
  "CREATE INDEX IF NOT EXISTS idx_usuarios_numero ON usuarios_privilegiados(numero)",
157
  "CREATE INDEX IF NOT EXISTS idx_transicoes_numero ON transicoes_humor(numero, timestamp)",
158
  "CREATE INDEX IF NOT EXISTS idx_girias_numero ON girias_aprendidas(numero, giria)",
159
  "CREATE INDEX IF NOT EXISTS idx_training_qualidade ON training_examples(qualidade_score, usado)",
160
  "CREATE INDEX IF NOT EXISTS idx_comandos_numero ON comandos_executados(numero, timestamp)",
161
+ "CREATE INDEX IF NOT EXISTS idx_reset_numero ON reset_log(numero, timestamp)",
162
+ "CREATE INDEX IF NOT EXISTS idx_audio_usuario ON audio_transcricoes(numero_usuario, sucesso)",
163
+ "CREATE INDEX IF NOT EXISTS idx_grupo_contexto_id ON grupo_contexto(grupo_id, contexto_id)"
164
  ]
165
 
166
  for idx_query in indexes:
 
176
  logger.error(f"Erro ao verificar colunas/índices: {e}")
177
 
178
  # ================================================================
179
+ # INICIALIZAÇÃO COMPLETA COM NOVAS TABELAS
180
  # ================================================================
181
 
182
  def _init_db(self):
 
184
  with self._get_connection() as conn:
185
  c = conn.cursor()
186
 
187
+ # TABELA PRINCIPAL DE MENSAGENS (ATUALIZADA)
188
  c.execute('''
189
  CREATE TABLE IF NOT EXISTS mensagens (
190
  id INTEGER PRIMARY KEY AUTOINCREMENT,
191
  usuario TEXT NOT NULL,
192
+ usuario_nome TEXT DEFAULT '',
193
  mensagem TEXT NOT NULL,
194
  resposta TEXT NOT NULL,
195
  numero TEXT NOT NULL,
196
  contexto_id TEXT NOT NULL,
197
  tipo_contexto TEXT DEFAULT 'pv',
198
+ tipo_mensagem TEXT DEFAULT 'texto',
199
  is_reply BOOLEAN DEFAULT 0,
200
  mensagem_original TEXT,
201
  reply_to_bot BOOLEAN DEFAULT 0,
202
+ reply_info_json TEXT,
203
  humor TEXT DEFAULT 'normal_ironico',
204
  modo_resposta TEXT DEFAULT 'normal_ironico',
205
  emocao_detectada TEXT,
206
  confianca_emocao REAL DEFAULT 0.5,
207
+ grupo_id TEXT DEFAULT '',
208
+ grupo_nome TEXT DEFAULT '',
209
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
210
  deletado BOOLEAN DEFAULT 0
211
  )
 
231
  )
232
  ''')
233
 
234
+ # TABELA DE CONTEXTO (ATUALIZADA)
235
  c.execute('''
236
  CREATE TABLE IF NOT EXISTS contexto (
237
  id INTEGER PRIMARY KEY AUTOINCREMENT,
 
248
  tom TEXT DEFAULT 'normal',
249
  emocao_tendencia TEXT DEFAULT 'neutral',
250
  volatilidade REAL DEFAULT 0.5,
251
+ nome_usuario TEXT DEFAULT '',
252
+ ultimo_contato DATETIME,
253
+ data_criacao DATETIME DEFAULT CURRENT_TIMESTAMP,
254
+ data_atualizacao DATETIME DEFAULT CURRENT_TIMESTAMP
255
+ )
256
+ ''')
257
+
258
+ # NOVA TABELA: CONTEXTO DE GRUPO
259
+ c.execute('''
260
+ CREATE TABLE IF NOT EXISTS grupo_contexto (
261
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
262
+ grupo_id TEXT UNIQUE NOT NULL,
263
+ contexto_id TEXT NOT NULL,
264
+ grupo_nome TEXT DEFAULT '',
265
+ numero_participantes INTEGER DEFAULT 0,
266
+ historico_grupo TEXT,
267
+ humor_medio TEXT DEFAULT 'normal_ironico',
268
+ tom_medio TEXT DEFAULT 'normal',
269
+ atividade_score REAL DEFAULT 0.5,
270
+ ultima_atividade DATETIME DEFAULT CURRENT_TIMESTAMP,
271
  data_criacao DATETIME DEFAULT CURRENT_TIMESTAMP,
272
  data_atualizacao DATETIME DEFAULT CURRENT_TIMESTAMP
273
  )
 
320
  )
321
  ''')
322
 
323
+ # NOVA TABELA: TRANSCRIÇÕES DE ÁUDIO
324
+ c.execute('''
325
+ CREATE TABLE IF NOT EXISTS audio_transcricoes (
326
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
327
+ numero_usuario TEXT NOT NULL,
328
+ audio_hash TEXT NOT NULL,
329
+ texto_transcrito TEXT NOT NULL,
330
+ fonte TEXT DEFAULT 'deepgram',
331
+ sucesso BOOLEAN DEFAULT 1,
332
+ confianca REAL DEFAULT 0.5,
333
+ duracao_segundos REAL,
334
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
335
+ )
336
+ ''')
337
+
338
  # TABELA DE COMANDOS EXECUTADOS
339
  c.execute('''
340
  CREATE TABLE IF NOT EXISTS comandos_executados (
 
363
 
364
  conn.commit()
365
 
366
+ logger.info("✅ Todas as tabelas criadas/verificadas (ADAPTADO)")
367
 
368
  except Exception as e:
369
  logger.error(f"❌ Erro ao criar tabelas: {e}")
 
599
  tabelas_para_resetar = [
600
  "mensagens", "contexto", "transicoes_humor",
601
  "girias_aprendidas", "training_examples",
602
+ "comandos_executados", "reset_log",
603
+ "audio_transcricoes", "grupo_contexto"
604
  ]
605
 
606
  for tabela in tabelas_para_resetar:
 
623
  }
624
 
625
  # ================================================================
626
+ # MÉTODOS DE RECUPERAÇÃO (COM ISOLAMENTO) - ADAPTADOS
627
  # ================================================================
628
 
629
  def recuperar_mensagens(self, numero: str, limite: int = 10) -> List[Tuple]:
 
631
  results = self._execute_with_retry(
632
  """
633
  SELECT mensagem, resposta, is_reply, mensagem_original, reply_to_bot,
634
+ humor, modo_resposta, emocao_detectada, confianca_emocao,
635
+ tipo_mensagem, reply_info_json
636
  FROM mensagens
637
  WHERE numero = ? AND deletado = 0
638
  ORDER BY timestamp DESC
 
643
  )
644
 
645
  if results:
646
+ mensagens = []
647
+ for r in results[::-1]:
648
+ try:
649
+ reply_info = json.loads(r[10]) if r[10] else None
650
+ except:
651
+ reply_info = None
652
+
653
+ mensagens.append((
654
  r[0], r[1], bool(r[2]), r[3], bool(r[4]),
655
+ r[5], r[6], r[7], r[8], r[9], reply_info
656
+ ))
657
+ return mensagens
 
658
  return []
659
 
660
  except Exception as e:
 
689
  """
690
  SELECT contexto_id, tipo_contexto, historico, humor_atual,
691
  modo_resposta, nivel_transicao, humor_alvo, termos,
692
+ girias, tom, emocao_tendencia, volatilidade, nome_usuario
693
  FROM contexto
694
  WHERE numero = ?
695
  """,
 
711
  "girias": row[8],
712
  "tom": row[9] or "normal",
713
  "emocao_tendencia": row[10] or "neutral",
714
+ "volatilidade": row[11] or 0.5,
715
+ "nome_usuario": row[12] or ""
716
  }
717
 
718
  return {
 
727
  "girias": "",
728
  "tom": "normal",
729
  "emocao_tendencia": "neutral",
730
+ "volatilidade": 0.5,
731
+ "nome_usuario": ""
732
  }
733
 
734
  except Exception as e:
 
745
  "girias": "",
746
  "tom": "normal",
747
  "emocao_tendencia": "neutral",
748
+ "volatilidade": 0.5,
749
+ "nome_usuario": ""
750
  }
751
 
752
  def recuperar_historico_humor(self, numero: str, limite: int = 5) -> List[Dict]:
 
851
  logger.error(f"Erro ao marcar exemplos: {e}")
852
 
853
  # ================================================================
854
+ # MÉTODOS DE SALVAMENTO (COM CONTEXTO) - ADAPTADOS
855
  # ================================================================
856
 
857
  def salvar_mensagem(self, usuario: str, mensagem: str, resposta: str,
 
860
  humor: str = 'normal_ironico',
861
  modo_resposta: str = 'normal_ironico',
862
  emocao_detectada: str = None,
863
+ confianca_emocao: float = 0.5,
864
+ tipo_mensagem: str = 'texto',
865
+ reply_info: Dict = None,
866
+ usuario_nome: str = '',
867
+ grupo_id: str = '',
868
+ grupo_nome: str = ''):
869
+ """Método adaptado para o payload do index.js"""
870
  try:
871
  numero_final = str(numero or usuario).strip()
872
  contexto_id = self._gerar_contexto_id(numero_final, 'auto')
873
+ tipo_contexto = "grupo" if ("@g.us" in numero_final or "120363" in numero_final or grupo_id) else "pv"
874
+
875
+ # Converte reply_info para JSON
876
+ reply_info_json = json.dumps(reply_info) if reply_info else None
877
+
878
+ # Se for grupo, atualiza grupo_contexto
879
+ if grupo_id and tipo_contexto == "grupo":
880
+ self._atualizar_contexto_grupo(grupo_id, grupo_nome or "Sem nome", usuario_nome or usuario, mensagem[:100])
881
 
882
  self._execute_with_retry(
883
  """
884
  INSERT INTO mensagens
885
+ (usuario, usuario_nome, mensagem, resposta, numero, contexto_id, tipo_contexto,
886
+ tipo_mensagem, is_reply, mensagem_original, reply_to_bot, reply_info_json,
887
+ humor, modo_resposta, emocao_detectada, confianca_emocao, grupo_id, grupo_nome)
888
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
889
  """,
890
  (
891
+ usuario[:50], usuario_nome[:100] or usuario[:100],
892
+ mensagem[:1000], resposta[:1000], numero_final,
893
+ contexto_id, tipo_contexto, tipo_mensagem, int(is_reply),
894
+ mensagem_original, int(reply_to_bot), reply_info_json,
895
+ humor, modo_resposta, emocao_detectada, confianca_emocao,
896
+ grupo_id[:50], grupo_nome[:100]
897
  ),
898
  commit=True,
899
  fetch=False
900
  )
901
 
902
+ logger.debug(f"✅ Mensagem salva: {numero_final} ({tipo_contexto}) | Tipo: {tipo_mensagem}")
903
  return True
904
 
905
  except Exception as e:
906
  logger.error(f"Erro ao salvar mensagem: {e}")
907
  return False
908
+
909
+ def _atualizar_contexto_grupo(self, grupo_id: str, grupo_nome: str, usuario: str, mensagem: str):
910
+ """Atualiza contexto de grupo"""
911
+ try:
912
+ contexto_id = self._gerar_contexto_id(grupo_id, 'grupo')
913
+
914
+ # Verifica se já existe
915
+ result = self._execute_with_retry(
916
+ "SELECT 1 FROM grupo_contexto WHERE grupo_id = ?",
917
+ (grupo_id,),
918
+ fetch=True
919
+ )
920
+
921
+ if result:
922
+ # Atualiza
923
+ self._execute_with_retry(
924
+ """
925
+ UPDATE grupo_contexto
926
+ SET ultima_atividade = CURRENT_TIMESTAMP,
927
+ data_atualizacao = CURRENT_TIMESTAMP
928
+ WHERE grupo_id = ?
929
+ """,
930
+ (grupo_id,),
931
+ commit=True,
932
+ fetch=False
933
+ )
934
+ else:
935
+ # Insere novo
936
+ self._execute_with_retry(
937
+ """
938
+ INSERT INTO grupo_contexto
939
+ (grupo_id, contexto_id, grupo_nome, ultima_atividade)
940
+ VALUES (?, ?, ?, CURRENT_TIMESTAMP)
941
+ """,
942
+ (grupo_id, contexto_id, grupo_nome[:100]),
943
+ commit=True,
944
+ fetch=False
945
+ )
946
+
947
+ except Exception as e:
948
+ logger.error(f"Erro ao atualizar contexto de grupo: {e}")
949
 
950
  def salvar_contexto(self, numero: str, **kwargs):
951
  try:
952
  numero_final = str(numero).strip()
953
  contexto_id = self._gerar_contexto_id(numero_final, 'auto')
954
  tipo_contexto = "grupo" if ("@g.us" in numero_final or "120363" in numero_final) else "pv"
955
+
956
+ # Adiciona nome_usuario se fornecido
957
+ if 'nome_usuario' in kwargs:
958
+ kwargs['ultimo_contato'] = "CURRENT_TIMESTAMP"
959
 
960
  campos = {
961
  "numero": numero_final,
 
1084
  except Exception as e:
1085
  logger.error(f"Erro ao salvar transição: {e}")
1086
 
1087
+ # ================================================================
1088
+ # NOVOS MÉTODOS PARA TRANSCRIÇÕES DE ÁUDIO
1089
+ # ================================================================
1090
+ def salvar_transcricao_audio(self, numero_usuario: str, audio_hash: str,
1091
+ texto_transcrito: str, fonte: str = "deepgram",
1092
+ sucesso: bool = True, confianca: float = 0.5,
1093
+ duracao_segundos: float = 0.0) -> bool:
1094
+ """Salva transcrição de áudio"""
1095
+ try:
1096
+ self._execute_with_retry(
1097
+ """
1098
+ INSERT INTO audio_transcricoes
1099
+ (numero_usuario, audio_hash, texto_transcrito, fonte, sucesso, confianca, duracao_segundos)
1100
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1101
+ """,
1102
+ (
1103
+ str(numero_usuario).strip(), audio_hash[:64], texto_transcrito[:5000],
1104
+ fonte[:50], int(sucesso), confianca, duracao_segundos
1105
+ ),
1106
+ commit=True,
1107
+ fetch=False
1108
+ )
1109
+ logger.info(f"✅ Transcrição de áudio salva: {numero_usuario} | Sucesso: {sucesso}")
1110
+ return True
1111
+ except Exception as e:
1112
+ logger.error(f"❌ Erro ao salvar transcrição: {e}")
1113
+ return False
1114
+
1115
+ def obter_historico_audio(self, numero_usuario: str, limite: int = 5) -> List[Dict]:
1116
+ """Recupera histórico de transcrições de áudio"""
1117
+ try:
1118
+ results = self._execute_with_retry(
1119
+ """
1120
+ SELECT texto_transcrito, fonte, sucesso, confianca, timestamp
1121
+ FROM audio_transcricoes
1122
+ WHERE numero_usuario = ?
1123
+ ORDER BY timestamp DESC
1124
+ LIMIT ?
1125
+ """,
1126
+ (str(numero_usuario).strip(), limite),
1127
+ fetch=True
1128
+ )
1129
+
1130
+ return [
1131
+ {
1132
+ "texto": r[0],
1133
+ "fonte": r[1],
1134
+ "sucesso": bool(r[2]),
1135
+ "confianca": r[3],
1136
+ "timestamp": r[4]
1137
+ }
1138
+ for r in results
1139
+ ]
1140
+ except Exception as e:
1141
+ logger.error(f"Erro ao recuperar histórico de áudio: {e}")
1142
+ return []
1143
+
1144
  # ================================================================
1145
  # MÉTODOS FALTANTES ADICIONADOS (RESOLVEM OS ERROS)
1146
  # ================================================================
 
1175
  self._execute_with_retry(
1176
  """
1177
  INSERT INTO mensagens
1178
+ (usuario, usuario_nome, mensagem, resposta, numero, contexto_id, tipo_contexto,
1179
  humor, modo_resposta, emocao_detectada, confianca_emocao)
1180
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1181
  """,
1182
  (
1183
+ "SISTEMA_TOM", "SISTEMA",
1184
  f"[TOM_DETECTADO: {tom}] {mensagem_contexto or ''}",
1185
  "",
1186
  numero_final,
 
1197
  self.salvar_contexto(
1198
  numero=numero_final,
1199
  tom=tom,
1200
+ emocao_tendencia=tom,
1201
+ nome_usuario="SISTEMA"
1202
  )
1203
  logger.info(f"✅ Tom registrado: {tom} (confiança: {confianca:.2f}) para {numero_final}")
1204
  return True
 
1242
  logger.error(f"❌ Erro ao salvar aprendizado detalhado: {e}")
1243
  return False
1244
 
1245
+ # ================================================================
1246
+ # MÉTODOS ADICIONAIS PARA O INDEX.JS
1247
+ # ================================================================
1248
+ def obter_estatisticas_usuario(self, numero: str) -> Dict:
1249
+ """Retorna estatísticas do usuário"""
1250
+ try:
1251
+ # Conta mensagens
1252
+ result = self._execute_with_retry(
1253
+ "SELECT COUNT(*) FROM mensagens WHERE numero = ? AND deletado = 0",
1254
+ (str(numero).strip(),),
1255
+ fetch=True
1256
+ )
1257
+ total_mensagens = result[0][0] if result else 0
1258
+
1259
+ # Última atividade
1260
+ result = self._execute_with_retry(
1261
+ "SELECT MAX(timestamp) FROM mensagens WHERE numero = ?",
1262
+ (str(numero).strip(),),
1263
+ fetch=True
1264
+ )
1265
+ ultima_atividade = result[0][0] if result else None
1266
+
1267
+ # Transições de humor
1268
+ result = self._execute_with_retry(
1269
+ "SELECT COUNT(*) FROM transicoes_humor WHERE numero = ?",
1270
+ (str(numero).strip(),),
1271
+ fetch=True
1272
+ )
1273
+ transicoes_humor = result[0][0] if result else 0
1274
+
1275
+ # Gírias aprendidas
1276
+ result = self._execute_with_retry(
1277
+ "SELECT COUNT(*) FROM girias_aprendidas WHERE numero = ?",
1278
+ (str(numero).strip(),),
1279
+ fetch=True
1280
+ )
1281
+ girias_aprendidas = result[0][0] if result else 0
1282
+
1283
+ return {
1284
+ "total_mensagens": total_mensagens,
1285
+ "ultima_atividade": ultima_atividade,
1286
+ "transicoes_humor": transicoes_humor,
1287
+ "girias_aprendidas": girias_aprendidas,
1288
+ "privilegiado": self.is_usuario_privilegiado(numero)
1289
+ }
1290
+
1291
+ except Exception as e:
1292
+ logger.error(f"Erro ao obter estatísticas: {e}")
1293
+ return {
1294
+ "total_mensagens": 0,
1295
+ "ultima_atividade": None,
1296
+ "transicoes_humor": 0,
1297
+ "girias_aprendidas": 0,
1298
+ "privilegiado": False
1299
+ }
1300
+
1301
+ def limpar_mensagens_antigas(self, dias: int = 30) -> int:
1302
+ """Limpa mensagens antigas"""
1303
+ try:
1304
+ result = self._execute_with_retry(
1305
+ """
1306
+ DELETE FROM mensagens
1307
+ WHERE timestamp < datetime('now', ?)
1308
+ AND deletado = 0
1309
+ """,
1310
+ (f'-{dias} days',),
1311
+ commit=True,
1312
+ fetch=False
1313
+ )
1314
+ logger.info(f"🧹 Mensagens antigas ({dias} dias) limpas: {result} registros")
1315
+ return result
1316
+ except Exception as e:
1317
+ logger.error(f"Erro ao limpar mensagens antigas: {e}")
1318
+ return 0
1319
+
1320
  # ================================================================
1321
  # FECHAMENTO SEGURO DA CONEXÃO
1322
  # ================================================================