From ae8436b01ee319b4266f84f1e95abeb0b436ad70 Mon Sep 17 00:00:00 2001 From: Marco Gallegos Date: Mon, 15 Dec 2025 19:11:09 -0600 Subject: [PATCH] feat: Integrate Google Sheets for duplicate chat ID verification and update main actions keyboard logic --- .env.example | 3 +++ main.py | 7 +++--- modules/database.py | 52 +++++++++++++++++++++++++++++++++++++++++++ modules/onboarding.py | 15 +++++++++++-- modules/ui.py | 17 +++++++++----- requirements.txt | 4 +++- 6 files changed, 86 insertions(+), 12 deletions(-) diff --git a/.env.example b/.env.example index f888f14..c3662e9 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,9 @@ TELEGRAM_ADMIN_CHAT_ID=TELEGRAM_ADMIN_CHAT_ID OPENAI_API_KEY=SK...... GOOGLE_API_KEY=AIzaSyBqH5... +# URL de la hoja de cálculo de Google para verificar duplicados +GOOGLE_SHEET_URL=https://docs.google.com/spreadsheets/d/1iVHnNoAF4sVVhb2kcclthznYFUKetmhsM6b2ZUCXd-0/edit?gid=370216950#gid=370216950 + # Webhooks de n8n (puedes agregar más aquí en el futuro) # Usa WEBHOOK_ONBOARDING (o el alias WEBHOOK_CONTRATO si ya lo tienes así) diff --git a/main.py b/main.py index d7eada9..f8dfb07 100644 --- a/main.py +++ b/main.py @@ -18,7 +18,7 @@ from telegram.ext import Application, Defaults, CommandHandler, ContextTypes # --- IMPORTAR HABILIDADES --- from modules.onboarding import onboarding_handler from modules.rh_requests import vacaciones_handler, permiso_handler -from modules.database import log_request +from modules.database import log_request, chat_id_exists # Importar chat_id_exists from modules.ui import main_actions_keyboard # from modules.finder import finder_handler (Si lo creas después) @@ -79,13 +79,14 @@ async def menu_principal(update: Update, context: ContextTypes.DEFAULT_TYPE): "👩‍💼 **Hola, soy Vanessa. ¿En qué puedo ayudarte hoy?**\n\n" "Toca un botón para continuar 👇" ) - await update.message.reply_text(texto, reply_markup=main_actions_keyboard()) + is_registered = chat_id_exists(user.id) + await update.message.reply_text(texto, reply_markup=main_actions_keyboard(is_registered=is_registered)) async def post_init(application: Application): # Mantén los comandos rápidos disponibles en el menú de Telegram await application.bot.set_my_commands([ BotCommand("start", "Mostrar menú principal"), - BotCommand("welcome", "Registro de nuevas empleadas"), + # BotCommand("welcome", "Registro de nuevas empleadas"), # Se maneja dinámicamente BotCommand("vacaciones", "Solicitar vacaciones"), BotCommand("permiso", "Solicitar permiso por horas"), BotCommand("links", "Links útiles"), diff --git a/modules/database.py b/modules/database.py index a0365f1..6e14d24 100644 --- a/modules/database.py +++ b/modules/database.py @@ -4,10 +4,62 @@ from datetime import datetime from sqlalchemy import Column, DateTime, Integer, MetaData, String, create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker +import gspread +from google.oauth2.service_account import Credentials # Configuración de logging logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") +# --- GOOGLE SHEETS SETUP --- +GSHEET_URL = os.getenv("GOOGLE_SHEET_URL") +GOOGLE_CREDENTIALS_FILE = os.getenv("GOOGLE_CREDENTIALS_FILE", "google_credentials.json") +SHEET_COLUMN_INDEX = 40 # AN is the 40th column + +def get_gsheet_client(): + """Retorna un cliente de gspread autenticado o None si falla.""" + if not GSHEET_URL: + logging.warning("GOOGLE_SHEET_URL no está configurada. La verificación de duplicados está deshabilitada.") + return None + + if not os.path.exists(GOOGLE_CREDENTIALS_FILE): + logging.warning(f"No se encontró el archivo de credenciales '{GOOGLE_CREDENTIALS_FILE}'. La verificación de duplicados está deshabilitada.") + return None + + try: + scopes = ["https://www.googleapis.com/auth/spreadsheets.readonly"] + creds = Credentials.from_service_account_file(GOOGLE_CREDENTIALS_FILE, scopes=scopes) + client = gspread.authorize(creds) + return client + except Exception as e: + logging.error(f"Error al autenticar con Google Sheets: {e}") + return None + +def chat_id_exists(chat_id: int) -> bool: + """Verifica si un chat_id de Telegram ya existe en la columna AN de la hoja de cálculo.""" + client = get_gsheet_client() + if not client: + return False # Si no hay cliente, no podemos verificar, así que asumimos que no existe. + + try: + spreadsheet = client.open_by_url(GSHEET_URL) + worksheet = spreadsheet.get_worksheet(0) # Primera hoja + + # Obtener todos los valores de la columna AN (índice 40) + chat_ids_in_sheet = worksheet.col_values(SHEET_COLUMN_INDEX) + + # El ID de chat puede venir como número o texto, así que comparamos como string + return str(chat_id) in chat_ids_in_sheet + + except gspread.exceptions.SpreadsheetNotFound: + logging.error(f"No se pudo encontrar la hoja de cálculo en la URL proporcionada.") + return False + except Exception as e: + logging.error(f"Error al leer la hoja de cálculo: {e}") + return False + + +# --- DATABASE (MySQL) SETUP --- + # Base para los modelos declarativos Base = declarative_base() diff --git a/modules/onboarding.py b/modules/onboarding.py index 0b4177b..da47618 100644 --- a/modules/onboarding.py +++ b/modules/onboarding.py @@ -17,7 +17,7 @@ from telegram.ext import ( Defaults, ) -from modules.database import log_request +from modules.database import log_request, chat_id_exists from modules.ui import main_actions_keyboard # --- 1. CARGA DE ENTORNO --- @@ -173,8 +173,19 @@ TECLADO_RELACION_EMERGENCIA = ReplyKeyboardMarkup( async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: user = update.effective_user - context.user_data.clear() log_request(user.id, user.username, "welcome", update.message.text) + + # --- VERIFICACIÓN DE DUPLICADOS --- + if chat_id_exists(user.id): + await update.message.reply_text( + "👩‍💼 Hola de nuevo. Ya tienes un registro activo en nuestro sistema.\n\n" + "Si crees que es un error o necesitas hacer cambios, por favor contacta a tu manager o a RH directamente. " + "¡Gracias!", + reply_markup=main_actions_keyboard() + ) + return ConversationHandler.END + + context.user_data.clear() context.user_data["metadata"] = { "telegram_id": user.id, diff --git a/modules/ui.py b/modules/ui.py index 7bb0c5d..d21e735 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1,13 +1,18 @@ from telegram import ReplyKeyboardMarkup -def main_actions_keyboard() -> ReplyKeyboardMarkup: +def main_actions_keyboard(is_registered: bool = False) -> ReplyKeyboardMarkup: """Teclado inferior con comandos directos (un toque lanza el flujo).""" + keyboard = [] + if not is_registered: + keyboard.append(["/welcome"]) + + keyboard.extend([ + ["/vacaciones", "/permiso"], + ["/links", "/start"], + ]) + return ReplyKeyboardMarkup( - [ - ["/welcome"], - ["/vacaciones", "/permiso"], - ["/links", "/start"], - ], + keyboard, resize_keyboard=True, ) diff --git a/requirements.txt b/requirements.txt index 46fc452..71bd7d6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,6 @@ requests SQLAlchemy mysql-connector-python google-generativeai -openai \ No newline at end of file +openai +gspread +google-auth-oauthlib \ No newline at end of file