Files
espiritismo-rag-system/backend/app/services/bot_service.py
T
lgcosta 7af7ba530d fix: make docker compose build and start the API
The image context was backend/, so COPY frontend/ failed. Build from the
repo root, place Python modules on the import path, and pin packages that
exist on PyPI. The web process no longer loads the LLM at startup.
2026-11-01 12:00:08 -03:00

215 lines
7.0 KiB
Python

import json
import os
import random
from typing import Dict, List
import telebot
class BotService:
def __init__(self, token: str, chat_id: str):
self.chat_id = chat_id
self.bot = telebot.TeleBot(token) if token else None
if self.bot is not None:
self._register_handlers()
# Store pending schedules
self.pending_schedules: List[Dict] = []
def _register_handlers(self):
"""Register message handlers."""
@self.bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
self.bot.reply_to(
message,
"📚 Sistema RAG Espiritismo\n\n"
"Comandos disponíveis:\n"
"/start - Iniciar\n"
"/help - Ajuda\n"
"/status - Ver status\n"
"/books - Listar livros\n"
)
@self.bot.message_handler(commands=['status'])
def send_status(message):
status_text = f"""
📚 Status do Sistema RAG Espiritismo
Bot: Ativo
Chat ID: {self.chat_id}
Direcione uma pergunta sobre espiritismo para eu responder!
"""
self.bot.reply_to(message, status_text)
@self.bot.message_handler(commands=['books'])
def list_books(message):
books = [
"📖 Livros disponíveis:\n\n",
"• A Gênese (Allan Kardec)\n",
"• Livro dos Espíritos (Allan Kardec)\n",
"• Livro dos Médiums (Allan Kardec)\n",
"• O Céu e o Inferno (Manuel Quintão)\n",
"• O Evangelho Segundo o Espiritismo (Allan Kardec)\n",
"• O Que é o Espiritismo (Allan Kardec)\n"
]
self.bot.reply_to(message, "".join(books))
@self.bot.message_handler(func=lambda msg: True)
def handle_question(message):
"""Handle user questions."""
question = message.text
# Send loading indicator
self.bot.reply_to(
message,
"🔍 Processando sua pergunta..."
)
# Send to RAG service asynchronously
self._query_rag_service(question, message.chat.id)
def _query_rag_service(self, question: str, chat_id: int):
"""Query RAG service and send response."""
try:
from app.services.rag_service import QdrantService, RAGService
qdrant = QdrantService(os.getenv("QDRANT_URL", "http://qdrant:6333"))
rag = RAGService(
qdrant,
model_name=os.getenv("GPT4ALL_MODEL", "/app/models/llama-3.2-3b-instruct-q4_0.gguf"),
)
result = rag.query(question, top_k=3)
# Format response
response_text = f"""
✅ Resposta:\n\n{result['response']}
📚 Fontes:
{self._format_sources(result['sources'])}
"""
self.bot.send_message(chat_id, response_text)
except Exception as e:
self.bot.send_message(
chat_id,
f"❌ Erro ao processar sua pergunta: {str(e)}"
)
def _format_sources(self, sources: List[Dict]) -> str:
"""Format source metadata."""
formatted = []
for source in sources:
meta = source.get('metadata', {})
title = meta.get('title', 'Desconhecido')
chunk = meta.get('chunk_id', '?')
formatted.append(f"• {title} (chunk {chunk})")
return "\n".join(formatted)
def add_schedule(self, schedule: Dict):
"""Add schedule to pending list and database."""
self.pending_schedules.append(schedule)
self._save_schedules()
def update_schedule(self, schedule: Dict):
"""Update schedule in pending list and database."""
for i, s in enumerate(self.pending_schedules):
if s['id'] == schedule['id']:
self.pending_schedules[i] = schedule
break
self._save_schedules()
def remove_schedule(self, schedule: Dict):
"""Remove schedule from pending list and database."""
self.pending_schedules = [s for s in self.pending_schedules if s['id'] != schedule['id']]
self._save_schedules()
def _save_schedules(self):
"""Save schedules to JSON file."""
schedules_path = "/app/schedules.json"
# Load existing schedules
schedules = []
if os.path.exists(schedules_path):
with open(schedules_path, "r") as f:
schedules = json.load(f)
# Update pending schedules
for pending in self.pending_schedules:
found = False
for i, s in enumerate(schedules):
if s['id'] == pending['id']:
schedules[i] = pending
found = True
break
if not found:
schedules.append(pending)
# Write back
with open(schedules_path, "w") as f:
json.dump(schedules, f, indent=2)
def start_polling(self):
"""Start the bot polling."""
if self.bot is None:
print("TELEGRAM_BOT_TOKEN ausente; polling não iniciado")
return
print("Starting Telegram bot polling...")
self.bot.infinity_polling(timeout=10, long_polling_timeout=5)
def send_scheduled_message(self, schedule: Dict):
"""Send scheduled message to Telegram."""
try:
from app.services.rag_service import QdrantService, RAGService
qdrant = QdrantService(os.getenv("QDRANT_URL", "http://qdrant:6333"))
rag = RAGService(
qdrant,
model_name=os.getenv("GPT4ALL_MODEL", "/app/models/llama-3.2-3b-instruct-q4_0.gguf"),
)
response = rag.query(schedule.get('book') or 'espiritismo', top_k=3)
message = f"""
⏰ Mensagem Agendada - {schedule.get('book', 'Todos os livros')}
{response['response']}
📚 Fontes:
{self._format_sources(response['sources'])}
"""
if self.bot is None:
print("TELEGRAM_BOT_TOKEN ausente; mensagem agendada não enviada")
return
self.bot.send_message(self.chat_id, message)
except Exception as e:
print(f"Error sending scheduled message: {e}")
def generate_daily_content(self):
"""Generate daily content for all books."""
books = [
"A Gênese",
"Livro dos Espíritos",
"Livro dos Médiums",
"O Céu e o Inferno",
"O Evangelho Segundo o Espiritismo",
"O Que é o Espiritismo"
]
selected_books = random.sample(books, 2)
book_names = "\n".join(f"• {book}" for book in selected_books)
message = f"""
🌟 Conteúdo Diário de Espiritismo
📚 Livros selecionados:
{book_names}
Este conteúdo foi gerado automaticamente pelo Sistema RAG Espiritismo.
"""
if self.bot is None:
print("TELEGRAM_BOT_TOKEN ausente; conteúdo diário não enviado")
return
self.bot.send_message(self.chat_id, message)