feat: Integrate Google Sheets for duplicate chat ID verification and update main actions keyboard logic

This commit is contained in:
Marco Gallegos
2025-12-15 19:11:09 -06:00
parent aad973a9c7
commit ae8436b01e
6 changed files with 86 additions and 12 deletions

View File

@@ -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í)

View File

@@ -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"),

View File

@@ -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()

View File

@@ -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,9 +173,20 @@ 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,
"username": user.username or "N/A",

View File

@@ -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)."""
return ReplyKeyboardMarkup(
[
["/welcome"],
keyboard = []
if not is_registered:
keyboard.append(["/welcome"])
keyboard.extend([
["/vacaciones", "/permiso"],
["/links", "/start"],
],
])
return ReplyKeyboardMarkup(
keyboard,
resize_keyboard=True,
)

View File

@@ -5,3 +5,5 @@ SQLAlchemy
mysql-connector-python
google-generativeai
openai
gspread
google-auth-oauthlib