mirror of
https://github.com/marcogll/telegram_new_socias.git
synced 2026-01-13 13:15:16 +00:00
refactor: Implement dynamic conversation flow builder from JSON
This commit refactors the bot's architecture to dynamically load and build conversation flows from JSON files instead of hardcoding them in Python. - Added to read flow definitions from the directory and dynamically build s. - Refactored to use the new flow builder and load all conversation handlers at startup. - Moved hardcoded links to environment variables for better configuration. - Updated to support conditional branching for 'Other' options, using a field to define state transitions. - Updated with the new link variables.
This commit is contained in:
@@ -13,6 +13,15 @@ WEBHOOK_PERMISOS=url
|
|||||||
WEBHOOK_PRINTS=url
|
WEBHOOK_PRINTS=url
|
||||||
WEBHOOK_SCHEDULE=url
|
WEBHOOK_SCHEDULE=url
|
||||||
|
|
||||||
|
# ===============================
|
||||||
|
# LINKS
|
||||||
|
# ===============================
|
||||||
|
LINK_CURSOS=https://cursos.vanityexperience.mx/dashboard-2/
|
||||||
|
LINK_SITIO=https://vanityexperience.mx/
|
||||||
|
LINK_AGENDA_IOS=https://apps.apple.com/us/app/fresha-for-business/id1455346253
|
||||||
|
LINK_AGENDA_ANDROID=https://play.google.com/store/apps/details?id=com.fresha.Business
|
||||||
|
|
||||||
|
|
||||||
# ===============================
|
# ===============================
|
||||||
# DATABASE SETUP
|
# DATABASE SETUP
|
||||||
# ===============================
|
# ===============================
|
||||||
|
|||||||
@@ -4,232 +4,318 @@
|
|||||||
{
|
{
|
||||||
"state": 0,
|
"state": 0,
|
||||||
"variable": "NOMBRE_SALUDO",
|
"variable": "NOMBRE_SALUDO",
|
||||||
"question": "Para empezar con el pie derecho, ¿cómo te gusta que te llamemos?",
|
"question": "¿Cómo te gusta que te llamemos?",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 1,
|
"state": 1,
|
||||||
"variable": "NOMBRE_COMPLETO",
|
"variable": "NOMBRE_COMPLETO",
|
||||||
"question": "¡Lindo nombre! ✨\n\nNecesito tus datos oficiales para el contrato.\n¿Cuáles son tus *nombres* (sin apellidos) tal cual aparecen en tu INE?",
|
"question": "Escribe tus nombres (SIN apellidos), exactamente como aparecen en tu INE.",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 2,
|
"state": 2,
|
||||||
"variable": "APELLIDO_PATERNO",
|
"variable": "APELLIDO_PATERNO",
|
||||||
"question": "¿Cuál es tu *apellido paterno*?",
|
"question": "Apellido paterno:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 3,
|
"state": 3,
|
||||||
"variable": "APELLIDO_MATERNO",
|
"variable": "APELLIDO_MATERNO",
|
||||||
"question": "¿Y tu *apellido materno*?",
|
"question": "Apellido materno:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 4,
|
"state": 4,
|
||||||
"variable": "CUMPLE_DIA",
|
"variable": "CUMPLE_DIA",
|
||||||
"question": "🎂 Hablemos de ti. ¿Qué *día* es tu cumpleaños? (Escribe el número, ej: 13)",
|
"question": "Fecha de nacimiento · Día (solo número, ej. 13)",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 5,
|
"state": 5,
|
||||||
"variable": "CUMPLE_MES",
|
"variable": "CUMPLE_MES",
|
||||||
"question": "¿De qué *mes*? 🎉",
|
"question": "Fecha de nacimiento · Mes",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": [
|
"options": ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
|
||||||
"Enero", "Febrero", "Marzo",
|
"next_step": 6
|
||||||
"Abril", "Mayo", "Junio",
|
|
||||||
"Julio", "Agosto", "Septiembre",
|
|
||||||
"Octubre", "Noviembre", "Diciembre"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 6,
|
"state": 6,
|
||||||
"variable": "CUMPLE_ANIO",
|
"variable": "CUMPLE_ANIO",
|
||||||
"question": "Entendido. ¿Y de qué *año*? 🗓️",
|
"question": "Fecha de nacimiento · Año (4 dígitos)",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 7
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 7,
|
"state": 7,
|
||||||
"variable": "ESTADO_NACIMIENTO",
|
"variable": "ESTADO_NACIMIENTO",
|
||||||
"question": "🇲🇽 ¿En qué *estado de la república* naciste?",
|
"question": "Estado de nacimiento\n\nSelecciona el estado donde naciste.\nSi no aparece, elige *Otro*.",
|
||||||
"type": "text"
|
"type": "keyboard",
|
||||||
|
"options": ["Coahuila", "Nuevo León", "Otro"],
|
||||||
|
"next_steps": [
|
||||||
|
{ "value": "Otro", "go_to": 7.1 },
|
||||||
|
{ "value": "default", "go_to": 8 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": 7.1,
|
||||||
|
"variable": "ESTADO_NACIMIENTO_OTRO",
|
||||||
|
"question": "Escribe el nombre del estado donde naciste.",
|
||||||
|
"type": "text",
|
||||||
|
"next_step": 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 8,
|
"state": 8,
|
||||||
"variable": "RFC",
|
"variable": "RFC",
|
||||||
"question": "Pasemos a lo administrativo 📄.\n\nPor favor escribe tu *RFC* (Sin espacios):",
|
"question": "RFC completo (13 caracteres, sin espacios):",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 9
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 9,
|
"state": 9,
|
||||||
"variable": "CURP",
|
"variable": "CURP",
|
||||||
"question": "Gracias. Ahora tu *CURP*:",
|
"question": "CURP completo (18 caracteres):",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 10,
|
"state": 10,
|
||||||
"variable": "CORREO",
|
"variable": "CORREO",
|
||||||
"question": "¡Súper! 📧 ¿A qué *correo electrónico* te enviamos la info?",
|
"question": "Correo electrónico personal:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 11,
|
"state": 11,
|
||||||
"variable": "CELULAR",
|
"variable": "CELULAR",
|
||||||
"question": "📱 ¿Cuál es tu número de *celular* personal? (10 dígitos)",
|
"question": "Número de celular (10 dígitos):",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 12
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 12,
|
"state": 12,
|
||||||
"variable": "CALLE",
|
"variable": "CALLE",
|
||||||
"question": "🏠 Registremos tu domicilio.\n\n¿En qué *calle* vives?",
|
"question": "Domicilio · Calle:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 13
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 13,
|
"state": 13,
|
||||||
"variable": "NUM_EXTERIOR",
|
"variable": "NUM_EXTERIOR",
|
||||||
"question": "#️⃣ ¿Cuál es el *número exterior*?",
|
"question": "Domicilio · Número exterior:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 14
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 14,
|
"state": 14,
|
||||||
"variable": "NUM_INTERIOR",
|
"variable": "NUM_INTERIOR",
|
||||||
"question": "🚪 ¿Tienes *número interior*? (Escribe 0 si no aplica)",
|
"question": "Domicilio · Número interior (0 si no aplica):",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 15
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 15,
|
"state": 15,
|
||||||
"variable": "COLONIA",
|
"variable": "COLONIA",
|
||||||
"question": "🏘️ ¿Cómo se llama la *colonia*?",
|
"question": "Domicilio · Colonia:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 16
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 16,
|
"state": 16,
|
||||||
"variable": "CODIGO_POSTAL",
|
"variable": "CODIGO_POSTAL",
|
||||||
"question": "📮 ¿Cuál es el *Código Postal*?",
|
"question": "Código Postal (5 dígitos):",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 17
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 17,
|
"state": 17,
|
||||||
"variable": "CIUDAD_RESIDENCIA",
|
"variable": "CIUDAD_RESIDENCIA",
|
||||||
"question": "¿En qué *ciudad* resides actualmente?",
|
"question": "Ciudad de residencia:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": ["Saltillo", "Ramos Arizpe", "Arteaga"]
|
"options": ["Saltillo", "Ramos Arizpe", "Arteaga", "Otro"],
|
||||||
|
"next_steps": [
|
||||||
|
{ "value": "Otro", "go_to": 17.1 },
|
||||||
|
{ "value": "default", "go_to": 18 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": 17.1,
|
||||||
|
"variable": "CIUDAD_RESIDENCIA_OTRO",
|
||||||
|
"question": "Escribe tu ciudad de residencia:",
|
||||||
|
"type": "text",
|
||||||
|
"next_step": 18
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 18,
|
"state": 18,
|
||||||
"variable": "ROL",
|
"variable": "ROL",
|
||||||
"question": "🔎 *Rol dentro del equipo*\nElige la opción que mejor describa tu posición:\n• *Belleza* — servicios de estética y spa\n• *Staff (Recepción)* — agenda y atención a clientes\n• *Marketing* — contenido, promos y comunidad\n\n_Toca un botón o escribe la opción:_",
|
"question": "Rol dentro del equipo:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": ["Belleza", "Staff (Recepción)", "Marketing"]
|
"options": ["Belleza", "Staff (Recepción)", "Marketing"],
|
||||||
|
"next_step": 19
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 19,
|
"state": 19,
|
||||||
"variable": "SUCURSAL",
|
"variable": "SUCURSAL",
|
||||||
"question": "¿A qué *sucursal* te vas a integrar? 📍",
|
"question": "Sucursal principal:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": ["Plaza Cima (Sur) ⛰️", "Plaza O (Carranza) 🏙️"]
|
"options": ["Plaza Cima (Sur)", "Plaza O (Carranza)"],
|
||||||
|
"next_step": 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 20,
|
"state": 20,
|
||||||
"variable": "INICIO_DIA",
|
"variable": "INICIO_DIA",
|
||||||
"question": "¡Qué emoción! 🎉\n\n¿Qué *día* está programado tu ingreso? (Solo el número, ej: 01)",
|
"question": "Fecha de ingreso · Día:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 21
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 21,
|
"state": 21,
|
||||||
"variable": "INICIO_MES",
|
"variable": "INICIO_MES",
|
||||||
"question": "¿De qué *mes* será tu ingreso?",
|
"question": "Fecha de ingreso · Mes:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": [
|
"options": ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
|
||||||
"Enero", "Febrero", "Marzo",
|
"next_step": 22
|
||||||
"Abril", "Mayo", "Junio",
|
|
||||||
"Julio", "Agosto", "Septiembre",
|
|
||||||
"Octubre", "Noviembre", "Diciembre"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 22,
|
"state": 22,
|
||||||
"variable": "INICIO_ANIO",
|
"variable": "INICIO_ANIO",
|
||||||
"question": "¿Y de qué *año*?",
|
"question": "Fecha de ingreso · Año:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": ["2020", "2021", "2022", "2023", "2024", "2025", "2026"]
|
"options": ["2024", "2025", "2026"],
|
||||||
|
"next_step": 23
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 23,
|
"state": 23,
|
||||||
"variable": "REF1_NOMBRE",
|
"variable": "REF1_NOMBRE",
|
||||||
"question": "Ya casi acabamos. Necesito 3 referencias.\n\n👤 *Referencia 1*: Nombre completo",
|
"question": "Referencia 1 · Nombre completo:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 24
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 24,
|
"state": 24,
|
||||||
"variable": "REF1_TELEFONO",
|
"variable": "REF1_TELEFONO",
|
||||||
"question": "📞 Teléfono de la Referencia 1:",
|
"question": "Referencia 1 · Teléfono:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 25
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 25,
|
"state": 25,
|
||||||
"variable": "REF1_TIPO",
|
"variable": "REF1_TIPO",
|
||||||
"question": "🧑🤝🧑 ¿Qué relación tienes con ella/él?",
|
"question": "Referencia 1 · Relación:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": ["Familiar", "Amistad", "Trabajo", "Académica", "Otra"]
|
"options": ["Familiar", "Amistad", "Trabajo", "Académica", "Otra"],
|
||||||
|
"next_steps": [
|
||||||
|
{ "value": "Otra", "go_to": 25.1 },
|
||||||
|
{ "value": "default", "go_to": 26 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": 25.1,
|
||||||
|
"variable": "REF1_TIPO_OTRA",
|
||||||
|
"question": "Especifíca la relación con la Referencia 1:",
|
||||||
|
"type": "text",
|
||||||
|
"next_step": 26
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 26,
|
"state": 26,
|
||||||
"variable": "REF2_NOMBRE",
|
"variable": "REF2_NOMBRE",
|
||||||
"question": "Ok. Vamos con la *Referencia 2*.\n\n👤 Nombre completo:",
|
"question": "Referencia 2 · Nombre completo:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 27
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 27,
|
"state": 27,
|
||||||
"variable": "REF2_TELEFONO",
|
"variable": "REF2_TELEFONO",
|
||||||
"question": "📞 Teléfono de la Referencia 2:",
|
"question": "Referencia 2 · Teléfono:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 28
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 28,
|
"state": 28,
|
||||||
"variable": "REF2_TIPO",
|
"variable": "REF2_TIPO",
|
||||||
"question": "🧑🤝🧑 ¿Qué relación tienen?",
|
"question": "Referencia 2 · Relación:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": ["Familiar", "Amistad", "Trabajo", "Académica", "Otra"]
|
"options": ["Familiar", "Amistad", "Trabajo", "Académica", "Otra"],
|
||||||
|
"next_steps": [
|
||||||
|
{ "value": "Otra", "go_to": 28.1 },
|
||||||
|
{ "value": "default", "go_to": 29 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": 28.1,
|
||||||
|
"variable": "REF2_TIPO_OTRA",
|
||||||
|
"question": "Especifíca la relación con la Referencia 2:",
|
||||||
|
"type": "text",
|
||||||
|
"next_step": 29
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 29,
|
"state": 29,
|
||||||
"variable": "REF3_NOMBRE",
|
"variable": "REF3_NOMBRE",
|
||||||
"question": "Última. *Referencia 3*.\n\n👤 Nombre completo:",
|
"question": "Referencia 3 · Nombre completo:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 30
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 30,
|
"state": 30,
|
||||||
"variable": "REF3_TELEFONO",
|
"variable": "REF3_TELEFONO",
|
||||||
"question": "📞 Teléfono de la Referencia 3:",
|
"question": "Referencia 3 · Teléfono:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 31
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 31,
|
"state": 31,
|
||||||
"variable": "REF3_TIPO",
|
"variable": "REF3_TIPO",
|
||||||
"question": "🧑🤝🧑 ¿Qué relación tienen?",
|
"question": "Referencia 3 · Relación:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": ["Familiar", "Amistad", "Trabajo", "Académica", "Otra"]
|
"options": ["Familiar", "Amistad", "Trabajo", "Académica", "Otra"],
|
||||||
|
"next_steps": [
|
||||||
|
{ "value": "Otra", "go_to": 31.1 },
|
||||||
|
{ "value": "default", "go_to": 32 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": 31.1,
|
||||||
|
"variable": "REF3_TIPO_OTRA",
|
||||||
|
"question": "Especifíca la relación con la Referencia 3:",
|
||||||
|
"type": "text",
|
||||||
|
"next_step": 32
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 32,
|
"state": 32,
|
||||||
"variable": "EMERGENCIA_NOMBRE",
|
"variable": "EMERGENCIA_NOMBRE",
|
||||||
"question": "Finalmente, por seguridad 🚑:\n\n¿A quién llamamos en caso de *emergencia*?",
|
"question": "Contacto de emergencia · Nombre completo:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 33
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 33,
|
"state": 33,
|
||||||
"variable": "EMERGENCIA_TEL",
|
"variable": "EMERGENCIA_TEL",
|
||||||
"question": "☎️ ¿Cuál es el teléfono de esa persona?",
|
"question": "Contacto de emergencia · Teléfono:",
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"next_step": 34
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"state": 34,
|
"state": 34,
|
||||||
"variable": "EMERGENCIA_RELACION",
|
"variable": "EMERGENCIA_RELACION",
|
||||||
"question": "¿Qué parentesco tiene contigo?",
|
"question": "Relación con el contacto de emergencia:",
|
||||||
"type": "keyboard",
|
"type": "keyboard",
|
||||||
"options": ["Padre/Madre", "Esposo/a","Pareja", "Hijo/a", "Hermano/a", "Amigo/a", "Otro"]
|
"options": ["Padre/Madre", "Esposo/a", "Pareja", "Hijo/a", "Hermano/a", "Amigo/a", "Otro"],
|
||||||
|
"next_steps": [
|
||||||
|
{ "value": "Otro", "go_to": 34.1 },
|
||||||
|
{ "value": "default", "go_to": -1 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": 34.1,
|
||||||
|
"variable": "EMERGENCIA_RELACION_OTRA",
|
||||||
|
"question": "Especifíca la relación con el contacto de emergencia:",
|
||||||
|
"type": "text",
|
||||||
|
"next_step": -1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
20
main.py
20
main.py
@@ -16,17 +16,18 @@ from telegram.constants import ParseMode
|
|||||||
from telegram.ext import Application, Defaults, CommandHandler, ContextTypes
|
from telegram.ext import Application, Defaults, CommandHandler, ContextTypes
|
||||||
|
|
||||||
# --- IMPORTAR HABILIDADES ---
|
# --- IMPORTAR HABILIDADES ---
|
||||||
from modules.onboarding import onboarding_handler
|
from modules.flow_builder import load_flows
|
||||||
from modules.rh_requests import vacaciones_handler, permiso_handler
|
|
||||||
from modules.logger import log_request
|
from modules.logger import log_request
|
||||||
from modules.database import chat_id_exists # Importar chat_id_exists
|
from modules.database import chat_id_exists # Importar chat_id_exists
|
||||||
from modules.ui import main_actions_keyboard
|
from modules.ui import main_actions_keyboard
|
||||||
# from modules.finder import finder_handler (Si lo creas después)
|
# from modules.finder import finder_handler (Si lo creas después)
|
||||||
|
|
||||||
LINK_CURSOS = "https://cursos.vanityexperience.mx/dashboard-2/"
|
# Cargar links desde variables de entorno
|
||||||
LINK_SITIO = "https://vanityexperience.mx/"
|
LINK_CURSOS = os.getenv("LINK_CURSOS", "https://cursos.vanityexperience.mx/dashboard-2/")
|
||||||
LINK_AGENDA_IOS = "https://apps.apple.com/us/app/fresha-for-business/id1455346253"
|
LINK_SITIO = os.getenv("LINK_SITIO", "https://vanityexperience.mx/")
|
||||||
LINK_AGENDA_ANDROID = "https://play.google.com/store/apps/details?id=com.fresha.Business"
|
LINK_AGENDA_IOS = os.getenv("LINK_AGENDA_IOS", "https://apps.apple.com/us/app/fresha-for-business/id1455346253")
|
||||||
|
LINK_AGENDA_ANDROID = os.getenv("LINK_AGENDA_ANDROID", "https://play.google.com/store/apps/details?id=com.fresha.Business")
|
||||||
|
|
||||||
|
|
||||||
TOKEN = os.getenv("TELEGRAM_TOKEN")
|
TOKEN = os.getenv("TELEGRAM_TOKEN")
|
||||||
|
|
||||||
@@ -112,9 +113,10 @@ def main():
|
|||||||
app.add_handler(CommandHandler("help", menu_principal))
|
app.add_handler(CommandHandler("help", menu_principal))
|
||||||
|
|
||||||
# 2. Habilidades Complejas (Conversaciones)
|
# 2. Habilidades Complejas (Conversaciones)
|
||||||
app.add_handler(onboarding_handler)
|
flow_handlers = load_flows()
|
||||||
app.add_handler(vacaciones_handler)
|
for handler in flow_handlers:
|
||||||
app.add_handler(permiso_handler)
|
app.add_handler(handler)
|
||||||
|
|
||||||
app.add_handler(CommandHandler("links", links_menu))
|
app.add_handler(CommandHandler("links", links_menu))
|
||||||
# app.add_handler(finder_handler)
|
# app.add_handler(finder_handler)
|
||||||
|
|
||||||
|
|||||||
134
modules/flow_builder.py
Normal file
134
modules/flow_builder.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from functools import partial
|
||||||
|
from telegram import Update, ReplyKeyboardRemove, ReplyKeyboardMarkup
|
||||||
|
from telegram.ext import (
|
||||||
|
ContextTypes,
|
||||||
|
ConversationHandler,
|
||||||
|
MessageHandler,
|
||||||
|
CommandHandler,
|
||||||
|
filters,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assuming finalization logic will be handled elsewhere for now
|
||||||
|
# from .onboarding import finalizar, cancelar
|
||||||
|
|
||||||
|
# A simple end state for now
|
||||||
|
async def end(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||||
|
await update.message.reply_text("Flow ended.")
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
async def generic_callback(update: Update, context: ContextTypes.DEFAULT_TYPE, flow: dict):
|
||||||
|
current_state_key = context.user_data.get("current_state", 0)
|
||||||
|
|
||||||
|
# Find the current step in the flow
|
||||||
|
current_step = next((step for step in flow["steps"] if step["state"] == current_state_key), None)
|
||||||
|
|
||||||
|
if not current_step:
|
||||||
|
await update.message.reply_text("Hubo un error en el flujo. Por favor, inicia de nuevo.")
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
# Save the answer
|
||||||
|
user_answer = update.message.text
|
||||||
|
variable_name = current_step.get("variable")
|
||||||
|
if variable_name:
|
||||||
|
context.user_data[variable_name] = user_answer
|
||||||
|
|
||||||
|
# Determine the next state
|
||||||
|
next_state_key = None
|
||||||
|
if "next_steps" in current_step:
|
||||||
|
for condition in current_step["next_steps"]:
|
||||||
|
if condition["value"] == user_answer:
|
||||||
|
next_state_key = condition["go_to"]
|
||||||
|
break
|
||||||
|
elif condition["value"] == "default":
|
||||||
|
next_state_key = condition["go_to"]
|
||||||
|
elif "next_step" in current_step:
|
||||||
|
next_state_key = current_step["next_step"]
|
||||||
|
|
||||||
|
if next_state_key is None:
|
||||||
|
# If no next step is defined, end the conversation
|
||||||
|
return await end(update, context)
|
||||||
|
|
||||||
|
# Find the next step
|
||||||
|
next_step = next((step for step in flow["steps"] if step["state"] == next_state_key), None)
|
||||||
|
|
||||||
|
if not next_step:
|
||||||
|
# If the next step is the end of the conversation
|
||||||
|
if next_state_key == -1:
|
||||||
|
# Here we would call the generic "finalizar" function
|
||||||
|
# For now, just end it
|
||||||
|
await update.message.reply_text("Has completado el flujo. ¡Gracias!")
|
||||||
|
# return await finalizar(update, context)
|
||||||
|
return ConversationHandler.END
|
||||||
|
else:
|
||||||
|
await update.message.reply_text("Error: No se encontró el siguiente paso del flujo.")
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
# Ask the next question
|
||||||
|
reply_markup = ReplyKeyboardRemove()
|
||||||
|
if next_step.get("type") == "keyboard" and "options" in next_step:
|
||||||
|
reply_markup = ReplyKeyboardMarkup(
|
||||||
|
[next_step["options"][i:i+3] for i in range(0, len(next_step["options"]), 3)],
|
||||||
|
one_time_keyboard=True, resize_keyboard=True
|
||||||
|
)
|
||||||
|
|
||||||
|
await update.message.reply_text(next_step["question"], reply_markup=reply_markup)
|
||||||
|
|
||||||
|
# Update the current state
|
||||||
|
context.user_data["current_state"] = next_state_key
|
||||||
|
return next_state_key
|
||||||
|
|
||||||
|
|
||||||
|
async def start_flow(update: Update, context: ContextTypes.DEFAULT_TYPE, flow: dict):
|
||||||
|
context.user_data.clear()
|
||||||
|
context.user_data["flow_name"] = flow["flow_name"]
|
||||||
|
|
||||||
|
# Start with the first step
|
||||||
|
first_step = flow["steps"][0]
|
||||||
|
context.user_data["current_state"] = first_step["state"]
|
||||||
|
|
||||||
|
reply_markup = ReplyKeyboardRemove()
|
||||||
|
if first_step.get("type") == "keyboard" and "options" in first_step:
|
||||||
|
reply_markup = ReplyKeyboardMarkup(
|
||||||
|
[first_step["options"][i:i+3] for i in range(0, len(first_step["options"]), 3)],
|
||||||
|
one_time_keyboard=True, resize_keyboard=True
|
||||||
|
)
|
||||||
|
|
||||||
|
await update.message.reply_text(first_step["question"], reply_markup=reply_markup)
|
||||||
|
return first_step["state"]
|
||||||
|
|
||||||
|
|
||||||
|
def create_handler(flow: dict):
|
||||||
|
states = {}
|
||||||
|
for step in flow["steps"]:
|
||||||
|
callback = partial(generic_callback, flow=flow)
|
||||||
|
states[step["state"]] = [MessageHandler(filters.TEXT & ~filters.COMMAND, callback)]
|
||||||
|
|
||||||
|
# The entry point should be a command with the same name as the flow
|
||||||
|
entry_point = CommandHandler(flow["flow_name"], partial(start_flow, flow=flow))
|
||||||
|
|
||||||
|
return ConversationHandler(
|
||||||
|
entry_points=[entry_point],
|
||||||
|
states=states,
|
||||||
|
fallbacks=[CommandHandler("cancelar", end)], # Replace with generic cancel
|
||||||
|
allow_reentry=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def load_flows():
|
||||||
|
flow_handlers = []
|
||||||
|
for filename in os.listdir("conv-flows"):
|
||||||
|
if filename.endswith(".json"):
|
||||||
|
filepath = os.path.join("conv-flows", filename)
|
||||||
|
with open(filepath, "r", encoding="utf-8") as f:
|
||||||
|
try:
|
||||||
|
flow_definition = json.load(f)
|
||||||
|
handler = create_handler(flow_definition)
|
||||||
|
flow_handlers.append(handler)
|
||||||
|
logging.info(f"Flow '{flow_definition['flow_name']}' loaded successfully.")
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logging.error(f"Error decoding JSON from {filename}: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error creating handler for {filename}: {e}")
|
||||||
|
return flow_handlers
|
||||||
Reference in New Issue
Block a user