mirror of
https://github.com/marcogll/talia_bot.git
synced 2026-01-13 21:35:19 +00:00
Merge branch 'main' into feat/vikunja-admin-menu-14354086038768774668
This commit is contained in:
@@ -3,11 +3,14 @@
|
||||
# Permite buscar espacios libres y crear eventos.
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
from google.oauth2 import service_account
|
||||
from googleapiclient.discovery import build
|
||||
from googleapiclient.errors import HttpError
|
||||
from config import GOOGLE_SERVICE_ACCOUNT_FILE, CALENDAR_ID
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Configuración de los permisos (SCOPES) para acceder al calendario
|
||||
SCOPES = ["https://www.googleapis.com/auth/calendar"]
|
||||
|
||||
@@ -120,6 +123,7 @@ def get_events(start_time, end_time, calendar_id=CALENDAR_ID):
|
||||
Obtiene la lista de eventos entre dos momentos.
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Llamando a la API de Google Calendar para {calendar_id}")
|
||||
events_result = (
|
||||
service.events()
|
||||
.list(
|
||||
@@ -131,7 +135,12 @@ def get_events(start_time, end_time, calendar_id=CALENDAR_ID):
|
||||
)
|
||||
.execute()
|
||||
)
|
||||
return events_result.get("items", [])
|
||||
events = events_result.get("items", [])
|
||||
logger.info(f"Se obtuvieron {len(events)} eventos de la API.")
|
||||
return events
|
||||
except HttpError as error:
|
||||
print(f"Ocurrió un error al obtener eventos: {error}")
|
||||
logger.error(f"Ocurrió un error al obtener eventos: {error}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"Error inesperado al obtener eventos: {e}")
|
||||
return []
|
||||
|
||||
35
app/main.py
35
app/main.py
@@ -2,6 +2,7 @@
|
||||
# Este es el archivo principal del bot. Aquí se inicia todo y se configuran los comandos.
|
||||
|
||||
import logging
|
||||
import asyncio
|
||||
from telegram import Update
|
||||
from telegram.ext import (
|
||||
Application,
|
||||
@@ -93,20 +94,26 @@ async def button_dispatcher(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
||||
'view_pending': view_pending,
|
||||
}
|
||||
|
||||
# Mapeo para botones de aprobación
|
||||
approval_handlers = {
|
||||
'approve': handle_approval_action,
|
||||
'reject': handle_approval_action,
|
||||
}
|
||||
|
||||
if query.data in async_handlers:
|
||||
await async_handlers[query.data](update, context)
|
||||
return
|
||||
elif query.data in sync_text_handlers:
|
||||
response_text = sync_text_handlers[query.data]()
|
||||
elif query.data in sync_markup_handlers:
|
||||
response_text, reply_markup = sync_markup_handlers[query.data]()
|
||||
elif query.data.startswith(tuple(approval_handlers.keys())):
|
||||
if query.data in simple_handlers:
|
||||
handler = simple_handlers[query.data]
|
||||
logger.info(f"Ejecutando simple_handler para: {query.data}")
|
||||
if asyncio.iscoroutinefunction(handler):
|
||||
response_text = await handler()
|
||||
else:
|
||||
response_text = handler()
|
||||
logger.info(f"Respuesta obtenida para {query.data}")
|
||||
await query.edit_message_text(text=response_text, parse_mode='Markdown')
|
||||
elif query.data in complex_handlers:
|
||||
handler = complex_handlers[query.data]
|
||||
logger.info(f"Ejecutando complex_handler para: {query.data}")
|
||||
if asyncio.iscoroutinefunction(handler):
|
||||
response_text, reply_markup = await handler()
|
||||
else:
|
||||
response_text, reply_markup = handler()
|
||||
logger.info(f"Respuesta obtenida para {query.data}")
|
||||
await query.edit_message_text(text=response_text, reply_markup=reply_markup, parse_mode='Markdown')
|
||||
elif query.data.startswith(('approve:', 'reject:')):
|
||||
logger.info(f"Ejecutando acción de aprobación: {query.data}")
|
||||
response_text = handle_approval_action(query.data)
|
||||
elif query.data == 'start_create_tag':
|
||||
response_text = "Para crear un tag, por favor usa el comando /create_tag."
|
||||
|
||||
@@ -3,19 +3,26 @@
|
||||
# Permite obtener y mostrar las actividades programadas para el día.
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
from google_calendar import get_events
|
||||
|
||||
def get_agenda():
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
async def get_agenda():
|
||||
"""
|
||||
Obtiene y muestra la agenda del usuario para el día actual desde Google Calendar.
|
||||
"""
|
||||
now = datetime.datetime.utcnow()
|
||||
try:
|
||||
logger.info("Obteniendo agenda...")
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = start_of_day + datetime.timedelta(days=1)
|
||||
|
||||
logger.info(f"Buscando eventos desde {start_of_day} hasta {end_of_day}")
|
||||
events = get_events(start_of_day, end_of_day)
|
||||
|
||||
if not events:
|
||||
logger.info("No se encontraron eventos.")
|
||||
return "📅 *Agenda para Hoy*\n\nNo tienes eventos programados para hoy."
|
||||
|
||||
agenda_text = "📅 *Agenda para Hoy*\n\n"
|
||||
@@ -30,4 +37,8 @@ def get_agenda():
|
||||
summary = event.get("summary", "(Sin título)")
|
||||
agenda_text += f"• *{time_str}* - {summary}\n"
|
||||
|
||||
logger.info("Agenda obtenida con éxito.")
|
||||
return agenda_text
|
||||
except Exception as e:
|
||||
logger.error(f"Error al obtener la agenda: {e}")
|
||||
return "❌ Error al obtener la agenda. Por favor, intenta de nuevo más tarde."
|
||||
|
||||
@@ -55,9 +55,7 @@ def handle_start(user_role):
|
||||
"""
|
||||
welcome_message = "Hola, soy Talía. ¿En qué puedo ayudarte hoy?"
|
||||
|
||||
if user_role == "owner":
|
||||
menu = get_owner_menu()
|
||||
elif user_role == "admin":
|
||||
if user_role in ["owner", "admin"]:
|
||||
menu = get_admin_menu()
|
||||
elif user_role == "team":
|
||||
menu = get_team_menu()
|
||||
|
||||
@@ -13,8 +13,8 @@ from telegram.ext import (
|
||||
ContextTypes,
|
||||
)
|
||||
|
||||
from app.config import VIKUNJA_API_URL, VIKUNJA_API_TOKEN
|
||||
from app.permissions import is_admin
|
||||
from config import VIKUNJA_API_URL, VIKUNJA_API_TOKEN
|
||||
from permissions import is_admin
|
||||
|
||||
# Configuración del logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
79
tasks.md
79
tasks.md
@@ -1,69 +1,22 @@
|
||||
# Talía Development Tasks
|
||||
# Plan de Corrección del Bot
|
||||
|
||||
This file tracks the development tasks for the Talía project.
|
||||
Este plan detalla los pasos necesarios para que el bot sea funcional, responda a los botones y no se bloquee.
|
||||
|
||||
## Phase 1: Project Scaffolding
|
||||
## 1. Corrección de Bloqueos (Agenda)
|
||||
- [ ] **Hacer asíncronas las llamadas a Google Calendar**: En `app/main.py`, usar `asyncio.to_thread` o un ejecutor para las funciones que bloquean el hilo principal (como `get_agenda` que llama a `get_events`).
|
||||
- [ ] **Optimizar `button_dispatcher`**: Asegurar que el despachador de botones maneje correctamente tanto funciones síncronas como asíncronas sin bloquearse.
|
||||
|
||||
- [x] Create `tasks.md` to track project development.
|
||||
- [x] Create the basic directory structure (`app`, `app/modules`).
|
||||
- [x] Create placeholder files in the root directory (`docker-compose.yml`, `Dockerfile`, `.env.example`).
|
||||
- [x] Create placeholder files in the `app` directory.
|
||||
- [x] Create placeholder files in the `app/modules` directory.
|
||||
## 2. Corrección de Botones y Flujos (ConversationHandlers)
|
||||
- [ ] **Vincular botones a conversaciones**: Modificar `app/modules/create_tag.py` y otros módulos para que sus `ConversationHandler` tengan como punto de entrada (`entry_points`) el `CallbackQueryHandler` del botón correspondiente.
|
||||
- [ ] **Limpiar `button_dispatcher`**: Eliminar las llamadas manuales a inicios de conversación dentro del despachador, dejando que los `ConversationHandler` se encarguen de capturar sus propios botones.
|
||||
|
||||
## Phase 2: Core Logic Implementation
|
||||
## 3. Robustez y Errores
|
||||
- [ ] **Manejador Global de Errores**: Añadir `application.add_error_handler` en `app/main.py` para capturar cualquier fallo y loguearlo, evitando que el bot se quede "pensando" sin dar respuesta.
|
||||
- [ ] **Verificación de Roles**: Asegurar que `onboarding.py` siempre entregue el menú correcto y que los IDs en `.env` estén bien cargados.
|
||||
|
||||
- [x] Implement `main.py` as the central orchestrator.
|
||||
- [x] Implement `config.py` to handle environment variables.
|
||||
- [x] Implement `permissions.py` for role-based access control.
|
||||
- [x] Implement `webhook_client.py` for n8n communication.
|
||||
## 4. Gestión de Procesos
|
||||
- [ ] **Evitar Conflictos**: Implementar una verificación al inicio de `main.py` para asegurar que no haya otra instancia del bot corriendo con el mismo token.
|
||||
|
||||
## Phase 3: Module Implementation
|
||||
|
||||
- [x] Implement `onboarding.py` module.
|
||||
- [x] Implement `agenda.py` module.
|
||||
- [x] Implement `citas.py` module.
|
||||
- [x] Implement `equipo.py` module.
|
||||
- [x] Implement `aprobaciones.py` module.
|
||||
- [x] Implement `servicios.py` module.
|
||||
- [x] Implement `admin.py` module.
|
||||
- [x] Add `/print` command for authorized users.
|
||||
|
||||
## Phase 4: Integrations
|
||||
|
||||
- [x] Implement `calendar.py` for Google Calendar integration.
|
||||
- [x] Implement `llm.py` for AI-powered responses.
|
||||
- [x] Implement `scheduler.py` for daily summaries.
|
||||
- [x] Implement `vikunja.py` module for task management.
|
||||
|
||||
## Phase 5: Refactoring and Bugfixing
|
||||
|
||||
- [x] Restructure admin menu into a two-level system.
|
||||
- [x] Refactor Vikunja module to integrate with the new admin menu.
|
||||
- [x] Add "edit task" functionality to the Vikunja module.
|
||||
- [x] Fix critical bug in `button_dispatcher` related to `async` function handling.
|
||||
- [x] Fix `create_tag` conversation handler integration.
|
||||
- [x] Stabilize and verify all admin menu functionalities.
|
||||
|
||||
## Log
|
||||
|
||||
### 2024-05-22
|
||||
|
||||
- Created `tasks.md` to begin tracking development.
|
||||
- Completed initial project scaffolding.
|
||||
- Implemented the core logic for the bot, including the central orchestrator, permissions, and onboarding.
|
||||
- Implemented the `agenda` and `citas` modules.
|
||||
- Implemented the conversational flow for proposing and approving activities.
|
||||
- Completed Phase 3 by implementing all modules and refactoring the main dispatcher.
|
||||
|
||||
### 2024-05-23
|
||||
|
||||
- Add `/print` command for authorized users.
|
||||
|
||||
### 2024-05-24
|
||||
|
||||
- Implemented Vikunja integration module.
|
||||
- Restructured the admin menu into a primary and secondary menu.
|
||||
- Added "edit task" functionality to the Vikunja module.
|
||||
- Fixed a critical bug in the `button_dispatcher` that was causing timeouts and unresponsive buttons.
|
||||
- Corrected the `create_tag` conversation handler to be initiated by a command instead of a button.
|
||||
- Verified that all admin menu options are now functioning correctly.
|
||||
## 5. Verificación Final
|
||||
- [ ] Probar cada botón del menú Admin: Agenda, Pendientes, Crear Tag, Más opciones.
|
||||
- [ ] Verificar que el bot responda "No hay eventos" o la lista de eventos sin colgarse.
|
||||
|
||||
27
test_calendar_debug.py
Normal file
27
test_calendar_debug.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import datetime
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add app directory to path
|
||||
sys.path.append(os.path.join(os.getcwd(), 'app'))
|
||||
|
||||
from google_calendar import get_events
|
||||
from config import CALENDAR_ID
|
||||
|
||||
def test_get_events():
|
||||
print(f"Testing with CALENDAR_ID: {CALENDAR_ID}")
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = start_of_day + datetime.timedelta(days=1)
|
||||
|
||||
print(f"Fetching events from {start_of_day.isoformat()} to {end_of_day.isoformat()}...")
|
||||
try:
|
||||
events = get_events(start_of_day, end_of_day)
|
||||
print(f"Found {len(events)} events.")
|
||||
for event in events:
|
||||
print(f"- {event.get('summary')} at {event['start'].get('dateTime', event['start'].get('date'))}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_get_events()
|
||||
Reference in New Issue
Block a user