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.
215 lines
7.0 KiB
Python
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)
|