mirror of
https://github.com/marcogll/talia_bot.git
synced 2026-03-15 18:25:59 +00:00
docs: Translate comments and logging messages to Spanish across various modules and the scheduler.
This commit is contained in:
@@ -1,18 +1,15 @@
|
||||
# app/modules/admin.py
|
||||
"""
|
||||
This module contains administrative functions for the bot.
|
||||
|
||||
Currently, it provides a simple way to check the system's status.
|
||||
"""
|
||||
# Este módulo contiene funciones administrativas para el bot.
|
||||
# Por ahora, permite ver el estado general del sistema.
|
||||
|
||||
def get_system_status():
|
||||
"""
|
||||
Returns a formatted string with the current status of the bot and its integrations.
|
||||
|
||||
This function currently returns a hardcoded status message. In the future,
|
||||
it could be expanded to perform real-time checks on the different services.
|
||||
Devuelve un mensaje con el estado actual del bot y sus conexiones.
|
||||
|
||||
Actualmente el mensaje es fijo (hardcoded), pero en el futuro podría
|
||||
hacer pruebas reales de conexión.
|
||||
"""
|
||||
# TODO: Implement real-time status checks for more accurate monitoring.
|
||||
# TODO: Implementar pruebas de estado en tiempo real para un monitoreo exacto.
|
||||
status_text = (
|
||||
"📊 *Estado del Sistema*\n\n"
|
||||
"- *Bot Principal:* Activo ✅\n"
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
# app/modules/agenda.py
|
||||
"""
|
||||
This module is responsible for handling agenda-related requests.
|
||||
|
||||
It provides functionality to fetch and display the user's schedule for the day.
|
||||
"""
|
||||
# Este módulo se encarga de manejar las peticiones relacionadas con la agenda.
|
||||
# Permite obtener y mostrar las actividades programadas para el día.
|
||||
|
||||
def get_agenda():
|
||||
"""
|
||||
Fetches and displays the user's agenda for the current day.
|
||||
|
||||
Currently, this function returns a hardcoded sample agenda for demonstration
|
||||
purposes. The plan is to replace this with a real integration that fetches
|
||||
events from a service like Google Calendar.
|
||||
Obtiene y muestra la agenda del usuario para el día actual.
|
||||
|
||||
Por ahora, esta función devuelve una agenda de ejemplo fija.
|
||||
El plan es conectarla con Google Calendar para que sea real.
|
||||
"""
|
||||
# TODO: Fetch the agenda dynamically from Google Calendar.
|
||||
# TODO: Obtener la agenda dinámicamente desde Google Calendar.
|
||||
agenda_text = (
|
||||
"📅 *Agenda para Hoy*\n\n"
|
||||
"• *10:00 AM - 11:00 AM*\n"
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
# app/modules/aprobaciones.py
|
||||
"""
|
||||
This module manages the approval workflow for requests made by the team.
|
||||
# Este módulo gestiona el flujo de aprobación para las solicitudes hechas por el equipo.
|
||||
# Permite ver solicitudes pendientes y aprobarlas o rechazarlas.
|
||||
# El usuario principal aquí es el "owner" (dueño).
|
||||
|
||||
It provides functions to view pending requests and to handle the approval or
|
||||
rejection of those requests. The primary user for this module is the "owner"
|
||||
role, who has the authority to approve or deny requests.
|
||||
"""
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
|
||||
def get_approval_menu(request_id):
|
||||
"""
|
||||
Creates and returns an inline keyboard with "Approve" and "Reject" buttons.
|
||||
|
||||
Each button is associated with a specific request_id through the
|
||||
callback_data, allowing the bot to identify which request is being acted upon.
|
||||
Crea un menú de botones (teclado en línea) con "Aprobar" y "Rechazar".
|
||||
|
||||
Cada botón lleva el ID de la solicitud para saber cuál estamos procesando.
|
||||
"""
|
||||
keyboard = [
|
||||
[
|
||||
# callback_data es lo que el bot recibe cuando se pulsa el botón
|
||||
InlineKeyboardButton("✅ Aprobar", callback_data=f'approve:{request_id}'),
|
||||
InlineKeyboardButton("❌ Rechazar", callback_data=f'reject:{request_id}'),
|
||||
]
|
||||
@@ -25,13 +22,11 @@ def get_approval_menu(request_id):
|
||||
|
||||
def view_pending():
|
||||
"""
|
||||
Shows the owner a list of pending requests that require their attention.
|
||||
|
||||
Currently, this function uses a hardcoded list of proposals for demonstration.
|
||||
In a production environment, this would fetch data from a database or another
|
||||
persistent storage mechanism where pending requests are tracked.
|
||||
Muestra al dueño una lista de solicitudes que esperan su aprobación.
|
||||
|
||||
Por ahora usa una lista fija de ejemplo.
|
||||
"""
|
||||
# TODO: Fetch pending requests dynamically from a database or webhook events.
|
||||
# TODO: Obtener solicitudes reales desde una base de datos o servicio externo.
|
||||
proposals = [
|
||||
{"id": "prop_001", "desc": "Grabación de proyecto", "duration": 4, "user": "Equipo A"},
|
||||
{"id": "prop_002", "desc": "Taller de guion", "duration": 2, "user": "Equipo B"},
|
||||
@@ -40,7 +35,7 @@ def view_pending():
|
||||
if not proposals:
|
||||
return "No hay solicitudes pendientes.", None
|
||||
|
||||
# For demonstration purposes, we'll just show the first pending proposal.
|
||||
# Tomamos la primera propuesta para mostrarla
|
||||
proposal = proposals[0]
|
||||
|
||||
text = (
|
||||
@@ -50,28 +45,25 @@ def view_pending():
|
||||
f"⏳ *Duración:* {proposal['duration']} horas"
|
||||
)
|
||||
|
||||
# Attach the approval menu to the message.
|
||||
# Adjuntamos los botones de aprobación
|
||||
reply_markup = get_approval_menu(proposal['id'])
|
||||
|
||||
return text, reply_markup
|
||||
|
||||
def handle_approval_action(callback_data):
|
||||
"""
|
||||
Handles the owner's response (approve or reject) to a request.
|
||||
|
||||
This function is triggered when the owner clicks one of the buttons created
|
||||
by get_approval_menu. It parses the callback_data to determine the action
|
||||
and the request ID.
|
||||
Maneja la respuesta del dueño (clic en aprobar o rechazar).
|
||||
|
||||
Separa la acción (approve/reject) del ID de la solicitud.
|
||||
"""
|
||||
# callback_data viene como "accion:id", por ejemplo "approve:prop_001"
|
||||
action, request_id = callback_data.split(':')
|
||||
|
||||
if action == 'approve':
|
||||
# TODO: Implement logic to update the request's status to 'approved'.
|
||||
# This could involve updating a database and notifying the requester.
|
||||
# TODO: Guardar en base de datos que fue aprobada y avisar al equipo.
|
||||
return f"✅ La solicitud *{request_id}* ha sido aprobada."
|
||||
elif action == 'reject':
|
||||
# TODO: Implement logic to update the request's status to 'rejected'.
|
||||
# This could involve updating a database and notifying the requester.
|
||||
# TODO: Guardar en base de datos que fue rechazada y avisar al equipo.
|
||||
return f"❌ La solicitud *{request_id}* ha sido rechazada."
|
||||
|
||||
return "Acción desconocida.", None
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
# app/modules/citas.py
|
||||
"""
|
||||
This module handles appointment scheduling for clients.
|
||||
|
||||
It provides a simple way for users to get a link to an external scheduling
|
||||
service, such as Calendly or an n8n workflow.
|
||||
"""
|
||||
# Este módulo maneja la programación de citas para los clientes.
|
||||
# Permite a los usuarios obtener un enlace para agendar una reunión.
|
||||
|
||||
def request_appointment():
|
||||
"""
|
||||
Provides the user with a link to schedule an appointment.
|
||||
|
||||
Currently, this function returns a hardcoded placeholder link to Calendly.
|
||||
The intention is to replace this with a dynamic link generated by an n8n
|
||||
workflow or another scheduling service.
|
||||
Proporciona al usuario un enlace para agendar una cita.
|
||||
|
||||
Por ahora devuelve un enlace de ejemplo a Calendly.
|
||||
La idea es que sea un enlace dinámico generado por n8n.
|
||||
"""
|
||||
# TODO: Integrate with a real scheduling service or an n8n workflow to
|
||||
# provide a dynamic and personalized scheduling link.
|
||||
# TODO: Integrar con un servicio real o un flujo de n8n para dar un enlace personalizado.
|
||||
response_text = (
|
||||
"Para agendar una cita, por favor utiliza el siguiente enlace: \n\n"
|
||||
"[Enlace de Calendly](https://calendly.com/user/appointment-link)"
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
# app/modules/create_tag.py
|
||||
"""
|
||||
This module contains the functionality for the /create_tag command.
|
||||
# Este módulo permite crear un "tag" (etiqueta) con información del empleado.
|
||||
# Usa un ConversationHandler para hacer una serie de preguntas al usuario.
|
||||
# Al final, genera un código en Base64 que contiene toda la información en formato JSON.
|
||||
|
||||
It uses a ConversationHandler to guide the user through a series of questions
|
||||
to collect data (name, employee number, branch, and Telegram ID), and then
|
||||
generates a Base64-encoded JSON string from that data. This string is intended
|
||||
to be used for creating an NFC tag.
|
||||
"""
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
@@ -19,62 +15,56 @@ from telegram.ext import (
|
||||
filters,
|
||||
)
|
||||
|
||||
# Enable logging to monitor the bot's operation and for debugging.
|
||||
# Configuramos los logs para este archivo
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Define the states for the conversation. These states are used to track the
|
||||
# user's progress through the conversation and determine which handler function
|
||||
# should be executed next.
|
||||
# Definimos los estados de la conversación.
|
||||
# Cada número representa un paso en el proceso de preguntas.
|
||||
NAME, NUM_EMP, SUCURSAL, TELEGRAM_ID = range(4)
|
||||
|
||||
async def create_tag_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Starts the conversation to create a new tag when the /create_tag command
|
||||
is issued. It prompts the user for the first piece of information (name).
|
||||
Inicia el proceso cuando el usuario escribe /create_tag.
|
||||
Pide el primer dato: el nombre.
|
||||
"""
|
||||
await update.message.reply_text("Vamos a crear un nuevo tag. Por favor, dime el nombre:")
|
||||
# The function returns the next state, which is NAME, so the conversation
|
||||
# knows which handler to call next.
|
||||
# Devolvemos el siguiente estado: NAME
|
||||
return NAME
|
||||
|
||||
async def get_name(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Stores the user's provided name in the context and then asks for the
|
||||
next piece of information, the employee number.
|
||||
Guarda el nombre y pide el número de empleado.
|
||||
"""
|
||||
context.user_data['name'] = update.message.text
|
||||
await update.message.reply_text("Gracias. Ahora, por favor, dime el número de empleado:")
|
||||
# The function returns the next state, NUM_EMP.
|
||||
# Devolvemos el siguiente estado: NUM_EMP
|
||||
return NUM_EMP
|
||||
|
||||
async def get_num_emp(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Stores the employee number and proceeds to ask for the branch name.
|
||||
Guarda el número de empleado y pide la sucursal.
|
||||
"""
|
||||
context.user_data['num_emp'] = update.message.text
|
||||
await update.message.reply_text("Entendido. Ahora, por favor, dime la sucursal:")
|
||||
# The function returns the next state, SUCURSAL.
|
||||
# Devolvemos el siguiente estado: SUCURSAL
|
||||
return SUCURSAL
|
||||
|
||||
async def get_sucursal(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Stores the branch name and asks for the final piece of information,
|
||||
the user's Telegram ID.
|
||||
Guarda la sucursal y pide el ID de Telegram.
|
||||
"""
|
||||
context.user_data['sucursal'] = update.message.text
|
||||
await update.message.reply_text("Perfecto. Finalmente, por favor, dime el ID de Telegram:")
|
||||
# The function returns the next state, TELEGRAM_ID.
|
||||
# Devolvemos el siguiente estado: TELEGRAM_ID
|
||||
return TELEGRAM_ID
|
||||
|
||||
async def get_telegram_id(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Stores the Telegram ID, assembles all the collected data into a JSON
|
||||
object, encodes it into a Base64 string, and sends the result back to
|
||||
the user. This function concludes the conversation.
|
||||
Guarda el ID de Telegram, junta todos los datos y genera el código Base64.
|
||||
"""
|
||||
context.user_data['telegram_id'] = update.message.text
|
||||
|
||||
# Create a dictionary from the data collected and stored in user_data.
|
||||
# Creamos un diccionario (como una caja con etiquetas) con todos los datos
|
||||
tag_data = {
|
||||
"name": context.user_data.get('name'),
|
||||
"num_emp": context.user_data.get('num_emp'),
|
||||
@@ -82,28 +72,27 @@ async def get_telegram_id(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
||||
"telegram_id": context.user_data.get('telegram_id'),
|
||||
}
|
||||
|
||||
# Convert the Python dictionary into a JSON formatted string.
|
||||
# Convertimos el diccionario a una cadena de texto en formato JSON
|
||||
json_string = json.dumps(tag_data)
|
||||
|
||||
# Encode the JSON string into Base64. The string is first encoded to
|
||||
# UTF-8 bytes, which is then encoded to Base64 bytes, and finally
|
||||
# decoded back to a UTF-8 string for display.
|
||||
# Convertimos esa cadena a Base64 (un formato que se puede guardar en tags NFC)
|
||||
# 1. Codificamos a bytes (utf-8)
|
||||
# 2. Codificamos esos bytes a base64
|
||||
# 3. Convertimos de vuelta a texto para mostrarlo
|
||||
base64_bytes = base64.b64encode(json_string.encode('utf-8'))
|
||||
base64_string = base64_bytes.decode('utf-8')
|
||||
|
||||
await update.message.reply_text(f"¡Gracias! Aquí está tu tag en formato Base64:\n\n`{base64_string}`", parse_mode='Markdown')
|
||||
|
||||
# Clean up the user_data dictionary to ensure no data from this
|
||||
# conversation is accidentally used in another one.
|
||||
# Limpiamos los datos temporales del usuario
|
||||
context.user_data.clear()
|
||||
|
||||
# End the conversation.
|
||||
# Terminamos la conversación
|
||||
return ConversationHandler.END
|
||||
|
||||
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Cancels and ends the conversation if the user issues the /cancel command.
|
||||
It also clears any data that has been collected so far.
|
||||
Cancela el proceso si el usuario escribe /cancel.
|
||||
"""
|
||||
await update.message.reply_text("Creación de tag cancelada.")
|
||||
context.user_data.clear()
|
||||
@@ -111,19 +100,13 @@ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
|
||||
def create_tag_conv_handler():
|
||||
"""
|
||||
Creates and returns a ConversationHandler for the /create_tag command.
|
||||
This handler manages the entire conversational flow, from starting the
|
||||
conversation to handling user inputs and ending the conversation.
|
||||
Configura el manejador de la conversación (el flujo de preguntas).
|
||||
"""
|
||||
return ConversationHandler(
|
||||
# The entry_points list defines how the conversation can be started.
|
||||
# In this case, it's started by the /create_tag command.
|
||||
# Punto de entrada: el comando /create_tag
|
||||
entry_points=[CommandHandler('create_tag', create_tag_start)],
|
||||
|
||||
# The states dictionary maps the conversation states to their
|
||||
# respective handler functions. When the conversation is in a
|
||||
# particular state, the corresponding handler is called to process
|
||||
# the user's message.
|
||||
# Mapa de estados: qué función responde a cada paso
|
||||
states={
|
||||
NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, get_name)],
|
||||
NUM_EMP: [MessageHandler(filters.TEXT & ~filters.COMMAND, get_num_emp)],
|
||||
@@ -131,12 +114,8 @@ def create_tag_conv_handler():
|
||||
TELEGRAM_ID: [MessageHandler(filters.TEXT & ~filters.COMMAND, get_telegram_id)],
|
||||
},
|
||||
|
||||
# The fallbacks list defines handlers that are called if the user
|
||||
# sends a message that doesn't match the current state's handler.
|
||||
# Here, it's used to handle the /cancel command.
|
||||
# Si algo falla o el usuario cancela
|
||||
fallbacks=[CommandHandler('cancel', cancel)],
|
||||
|
||||
# per_message=False means the conversation is tied to the user, not
|
||||
# to a specific message, which is standard for this type of flow.
|
||||
per_message=False
|
||||
)
|
||||
|
||||
@@ -1,45 +1,42 @@
|
||||
# app/modules/equipo.py
|
||||
"""
|
||||
This module contains functionality for authorized team members.
|
||||
# Este módulo contiene funciones para los miembros autorizados del equipo.
|
||||
# Incluye un flujo para proponer actividades que el dueño debe aprobar.
|
||||
|
||||
It includes a conversational flow for proposing new activities that require
|
||||
approval from the owner, as well as a function to check the status of
|
||||
previously submitted requests.
|
||||
"""
|
||||
from telegram import Update
|
||||
from telegram.ext import ContextTypes, ConversationHandler
|
||||
|
||||
# Define the states for the activity proposal conversation.
|
||||
# Definimos los estados para la conversación de propuesta de actividad.
|
||||
DESCRIPTION, DURATION = range(2)
|
||||
|
||||
async def propose_activity_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Starts the conversation for a team member to propose a new activity.
|
||||
This is typically triggered by an inline button press.
|
||||
Inicia el proceso para que un miembro del equipo proponga una actividad.
|
||||
Se activa cuando se pulsa el botón correspondiente.
|
||||
"""
|
||||
await update.callback_query.answer()
|
||||
await update.callback_query.edit_message_text(
|
||||
"Por favor, describe la actividad que quieres proponer."
|
||||
)
|
||||
# The function returns the next state, which is DESCRIPTION.
|
||||
# Siguiente paso: DESCRIPTION
|
||||
return DESCRIPTION
|
||||
|
||||
async def get_description(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Stores the activity description provided by the user and asks for the duration.
|
||||
Guarda la descripción de la actividad y pide la duración.
|
||||
"""
|
||||
context.user_data['activity_description'] = update.message.text
|
||||
await update.message.reply_text(
|
||||
"Entendido. Ahora, por favor, indica la duración estimada en horas (ej. 2, 4.5)."
|
||||
)
|
||||
# The function returns the next state, DURATION.
|
||||
# Siguiente paso: DURATION
|
||||
return DURATION
|
||||
|
||||
async def get_duration(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Stores the activity duration, confirms the proposal to the user, and ends the conversation.
|
||||
Guarda la duración, confirma la propuesta y termina la conversación.
|
||||
"""
|
||||
try:
|
||||
# Intentamos convertir el texto a un número decimal (float)
|
||||
duration = float(update.message.text)
|
||||
context.user_data['activity_duration'] = duration
|
||||
description = context.user_data.get('activity_description', 'N/A')
|
||||
@@ -51,24 +48,22 @@ async def get_duration(update: Update, context: ContextTypes.DEFAULT_TYPE) -> in
|
||||
"Recibirás una notificación cuando sea revisada."
|
||||
)
|
||||
|
||||
# TODO: Send this proposal to the owner for approval, for example,
|
||||
# by sending a webhook or saving it to a database.
|
||||
# TODO: Enviar esta propuesta al dueño (por webhook o base de datos).
|
||||
await update.message.reply_text(confirmation_text, parse_mode='Markdown')
|
||||
|
||||
# Clean up user_data to prevent data leakage into other conversations.
|
||||
# Limpiamos los datos temporales
|
||||
context.user_data.clear()
|
||||
|
||||
# End the conversation.
|
||||
# Terminamos la conversación
|
||||
return ConversationHandler.END
|
||||
except ValueError:
|
||||
# If the user provides an invalid number for the duration, ask again.
|
||||
# Si el usuario no escribe un número válido, se lo pedimos de nuevo
|
||||
await update.message.reply_text("Por favor, introduce un número válido para la duración en horas.")
|
||||
return DURATION
|
||||
|
||||
async def cancel_proposal(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
"""
|
||||
Cancels and ends the activity proposal conversation.
|
||||
This is triggered by the /cancel command.
|
||||
Cancela el proceso de propuesta si el usuario escribe /cancel.
|
||||
"""
|
||||
await update.message.reply_text("La propuesta de actividad ha sido cancelada.")
|
||||
context.user_data.clear()
|
||||
@@ -76,10 +71,9 @@ async def cancel_proposal(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
||||
|
||||
def view_requests_status():
|
||||
"""
|
||||
Allows a team member to see the status of their recent requests.
|
||||
|
||||
Currently, this returns a hardcoded sample status. In a real-world
|
||||
application, this would fetch the user's requests from a database.
|
||||
Permite a un miembro del equipo ver el estado de sus solicitudes recientes.
|
||||
|
||||
Por ahora devuelve un estado de ejemplo fijo.
|
||||
"""
|
||||
# TODO: Fetch the status of recent requests from a persistent data source.
|
||||
# TODO: Obtener el estado real desde una base de datos.
|
||||
return "Aquí está el estado de tus solicitudes recientes:\n\n- Grabación de proyecto (4h): Aprobado\n- Taller de guion (2h): Pendiente"
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
# app/modules/onboarding.py
|
||||
"""
|
||||
This module handles the initial interaction with the user, specifically the
|
||||
/start command.
|
||||
# Este módulo maneja la primera interacción con el usuario (el comando /start).
|
||||
# Se encarga de mostrar un menú diferente según quién sea el usuario (dueño, admin, equipo o cliente).
|
||||
|
||||
It is responsible for identifying the user's role and presenting them with a
|
||||
customized menu of options based on their permissions. This ensures that each
|
||||
user sees only the actions relevant to them.
|
||||
"""
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
|
||||
def get_owner_menu():
|
||||
"""Creates and returns the main menu keyboard for the 'owner' role."""
|
||||
"""Crea el menú de botones para el Dueño (Owner)."""
|
||||
keyboard = [
|
||||
[InlineKeyboardButton("📅 Ver mi agenda", callback_data='view_agenda')],
|
||||
[InlineKeyboardButton("⏳ Ver pendientes", callback_data='view_pending')],
|
||||
@@ -18,7 +13,7 @@ def get_owner_menu():
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
def get_admin_menu():
|
||||
"""Creates and returns the main menu keyboard for the 'admin' role."""
|
||||
"""Crea el menú de botones para los Administradores."""
|
||||
keyboard = [
|
||||
[InlineKeyboardButton("📊 Ver estado del sistema", callback_data='view_system_status')],
|
||||
[InlineKeyboardButton("👥 Gestionar usuarios", callback_data='manage_users')],
|
||||
@@ -26,7 +21,7 @@ def get_admin_menu():
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
def get_team_menu():
|
||||
"""Creates and returns the main menu keyboard for the 'team' role."""
|
||||
"""Crea el menú de botones para los Miembros del Equipo."""
|
||||
keyboard = [
|
||||
[InlineKeyboardButton("🕒 Proponer actividad", callback_data='propose_activity')],
|
||||
[InlineKeyboardButton("📄 Ver estatus de solicitudes", callback_data='view_requests_status')],
|
||||
@@ -34,7 +29,7 @@ def get_team_menu():
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
def get_client_menu():
|
||||
"""Creates and returns the main menu keyboard for the 'client' role."""
|
||||
"""Crea el menú de botones para los Clientes externos."""
|
||||
keyboard = [
|
||||
[InlineKeyboardButton("🗓️ Agendar una cita", callback_data='schedule_appointment')],
|
||||
[InlineKeyboardButton("ℹ️ Información de servicios", callback_data='get_service_info')],
|
||||
@@ -43,20 +38,18 @@ def get_client_menu():
|
||||
|
||||
def handle_start(user_role):
|
||||
"""
|
||||
Handles the /start command by sending a role-based welcome message and menu.
|
||||
|
||||
This function acts as a router, determining which menu to display based on
|
||||
the user's role, which is passed in as an argument.
|
||||
Decide qué mensaje y qué menú mostrar según el rol del usuario.
|
||||
"""
|
||||
welcome_message = "Hola, soy Talía. ¿En qué puedo ayudarte hoy?"
|
||||
|
||||
# Dependiendo del rol, llamamos a una función de menú diferente
|
||||
if user_role == "owner":
|
||||
menu = get_owner_menu()
|
||||
elif user_role == "admin":
|
||||
menu = get_admin_menu()
|
||||
elif user_role == "team":
|
||||
menu = get_team_menu()
|
||||
else: # Default to the client menu for any other role.
|
||||
else: # Por defecto, si no es ninguno de los anteriores, es un cliente
|
||||
menu = get_client_menu()
|
||||
|
||||
return welcome_message, menu
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
# app/modules/print.py
|
||||
"""
|
||||
This module provides a command for administrators to print out the current
|
||||
configuration details of the bot.
|
||||
# Este módulo permite a los administradores imprimir los detalles de configuración del bot.
|
||||
# Es una herramienta útil para depuración (debugging).
|
||||
|
||||
It is a debugging and administrative tool that allows authorized users to quickly
|
||||
inspect key configuration variables without accessing the environment directly.
|
||||
"""
|
||||
from telegram import Update
|
||||
from telegram.ext import ContextTypes
|
||||
from ..permissions import is_admin
|
||||
@@ -13,21 +9,22 @@ from ..config import TIMEZONE, CALENDAR_ID, N8N_WEBHOOK_URL
|
||||
|
||||
async def print_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
"""
|
||||
Handles the /print command.
|
||||
|
||||
When triggered, this function first checks if the user has admin privileges.
|
||||
If they do, it replies with a formatted message displaying the current values
|
||||
of the TIMEZONE, CALENDAR_ID, and N8N_WEBHOOK_URL configuration variables.
|
||||
If the user is not an admin, it sends a simple "not authorized" message.
|
||||
Maneja el comando /print.
|
||||
|
||||
Verifica si el usuario es administrador. Si lo es, muestra valores clave
|
||||
de la configuración (Zona horaria, ID de calendario, Webhook).
|
||||
"""
|
||||
chat_id = update.effective_chat.id
|
||||
|
||||
# Solo permitimos esto a los administradores
|
||||
if is_admin(chat_id):
|
||||
config_details = (
|
||||
f"**Configuration Details**\n"
|
||||
f"Timezone: `{TIMEZONE}`\n"
|
||||
f"Calendar ID: `{CALENDAR_ID}`\n"
|
||||
f"n8n Webhook URL: `{N8N_WEBHOOK_URL}`\n"
|
||||
f"**Detalles de Configuración**\n"
|
||||
f"Zona Horaria: `{TIMEZONE}`\n"
|
||||
f"ID de Calendario: `{CALENDAR_ID}`\n"
|
||||
f"URL Webhook n8n: `{N8N_WEBHOOK_URL}`\n"
|
||||
)
|
||||
await update.message.reply_text(config_details, parse_mode='Markdown')
|
||||
else:
|
||||
await update.message.reply_text("You are not authorized to use this command.")
|
||||
# Si no es admin, le avisamos que no tiene permiso
|
||||
await update.message.reply_text("No tienes autorización para usar este comando.")
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
# app/modules/servicios.py
|
||||
"""
|
||||
This module is responsible for providing information about the services offered.
|
||||
|
||||
It's a simple informational module that gives clients an overview of the
|
||||
available services and can be expanded to provide more detailed information
|
||||
or initiate a quoting process.
|
||||
"""
|
||||
# Este módulo se encarga de dar información sobre los servicios ofrecidos.
|
||||
# Es un módulo informativo para los clientes.
|
||||
|
||||
def get_service_info():
|
||||
"""
|
||||
Provides a brief overview of the available services.
|
||||
|
||||
Currently, this function returns a hardcoded list of services. For a more
|
||||
dynamic and easily maintainable system, this information could be fetched
|
||||
from a database, a configuration file, or an external API.
|
||||
Muestra una lista breve de los servicios disponibles.
|
||||
|
||||
Por ahora devuelve un texto fijo. Se podría conectar a una base de datos
|
||||
para que sea más fácil de actualizar.
|
||||
"""
|
||||
# TODO: Fetch service details from a database or a configuration file to
|
||||
# make the service list easier to manage and update.
|
||||
# TODO: Obtener detalles de servicios desde una base de datos o archivo de configuración.
|
||||
return "Ofrecemos una variedad de servicios, incluyendo:\n\n- Consultoría Estratégica\n- Desarrollo de Software\n- Talleres de Capacitación\n\n¿Sobre cuál te gustaría saber más?"
|
||||
|
||||
Reference in New Issue
Block a user