mirror of
https://github.com/marcogll/telegram_new_socias.git
synced 2026-01-13 21:25:16 +00:00
feat: Implement detailed onboarding data capture, refactor HR request date processing, and enhance bot command menu.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
import uuid
|
||||
from datetime import datetime, date
|
||||
@@ -24,105 +23,224 @@ def _send_webhooks(urls: list, payload: dict):
|
||||
print(f"[webhook] Error enviando a {url}: {e}")
|
||||
return enviados
|
||||
|
||||
TIPO_SOLICITITUD, FECHAS, MOTIVO = range(3)
|
||||
# Estados de conversación
|
||||
(
|
||||
VAC_ANIO,
|
||||
INICIO_DIA,
|
||||
INICIO_MES,
|
||||
FIN_DIA,
|
||||
FIN_MES,
|
||||
PERMISO_CUANDO,
|
||||
PERMISO_ANIO,
|
||||
HORARIO,
|
||||
MOTIVO,
|
||||
) = range(9)
|
||||
|
||||
# Teclados de apoyo
|
||||
MESES = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"]
|
||||
MESES = [
|
||||
"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
|
||||
"Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
|
||||
]
|
||||
TECLADO_MESES = ReplyKeyboardMarkup([MESES[i:i+3] for i in range(0, 12, 3)], one_time_keyboard=True, resize_keyboard=True)
|
||||
TECLADO_PERMISO_RAPIDO = ReplyKeyboardMarkup(
|
||||
[["Hoy 09:00-11:00", "Hoy 15:00-18:00"], ["Mañana 09:00-11:00", "Mañana 15:00-18:00"], ["Otra fecha/horario"]],
|
||||
MESES_MAP = {nombre.lower(): idx + 1 for idx, nombre in enumerate(MESES)}
|
||||
ANIO_ACTUAL = datetime.now().year
|
||||
TECLADO_ANIOS = ReplyKeyboardMarkup([[str(ANIO_ACTUAL), str(ANIO_ACTUAL + 1)]], one_time_keyboard=True, resize_keyboard=True)
|
||||
TECLADO_PERMISO_CUANDO = ReplyKeyboardMarkup(
|
||||
[["Hoy", "Mañana"], ["Pasado mañana", "Fecha específica"]],
|
||||
one_time_keyboard=True,
|
||||
resize_keyboard=True,
|
||||
)
|
||||
|
||||
def _calculate_vacation_metrics(date_string: str) -> dict:
|
||||
"""
|
||||
Calcula métricas de vacaciones a partir de un texto.
|
||||
Asume un formato como "10 al 15 de Octubre".
|
||||
"""
|
||||
def _parse_dia(texto: str) -> int:
|
||||
try:
|
||||
dia = int(texto)
|
||||
if 1 <= dia <= 31:
|
||||
return dia
|
||||
except Exception:
|
||||
pass
|
||||
return 0
|
||||
|
||||
def _parse_mes(texto: str) -> int:
|
||||
return MESES_MAP.get(texto.strip().lower(), 0)
|
||||
|
||||
def _parse_anio(texto: str) -> int:
|
||||
try:
|
||||
return int(texto)
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def _build_dates(datos: dict) -> dict:
|
||||
"""Construye fechas ISO; si fin < inicio, se ajusta a inicio."""
|
||||
year = datos.get("anio") or datetime.now().year
|
||||
try:
|
||||
inicio = date(year, datos["inicio_mes"], datos["inicio_dia"])
|
||||
fin_dia = datos.get("fin_dia", datos.get("inicio_dia"))
|
||||
fin_mes = datos.get("fin_mes", datos.get("inicio_mes"))
|
||||
fin = date(year, fin_mes, fin_dia)
|
||||
if fin < inicio:
|
||||
fin = inicio
|
||||
return {"inicio": inicio, "fin": fin}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
def _calculate_vacation_metrics_from_dates(fechas: dict) -> dict:
|
||||
today = date.today()
|
||||
current_year = today.year
|
||||
|
||||
# Mapeo de meses en español a número
|
||||
meses = {
|
||||
'enero': 1, 'febrero': 2, 'marzo': 3, 'abril': 4, 'mayo': 5, 'junio': 6,
|
||||
'julio': 7, 'agosto': 8, 'septiembre': 9, 'octubre': 10, 'noviembre': 11, 'diciembre': 12
|
||||
inicio = fechas.get("inicio")
|
||||
fin = fechas.get("fin")
|
||||
if not inicio or not fin:
|
||||
return {"dias_totales": 0, "dias_anticipacion": 0}
|
||||
dias_totales = (fin - inicio).days + 1
|
||||
dias_anticipacion = (inicio - today).days
|
||||
return {
|
||||
"dias_totales": dias_totales,
|
||||
"dias_anticipacion": dias_anticipacion,
|
||||
"fechas_calculadas": {"inicio": inicio.isoformat(), "fin": fin.isoformat()},
|
||||
}
|
||||
|
||||
# Regex para "10 al 15 de Octubre"
|
||||
match = re.search(r'(\d{1,2})\s*al\s*(\d{1,2})\s*de\s*(\w+)', date_string, re.IGNORECASE)
|
||||
|
||||
if not match:
|
||||
return {"dias_totales": 0, "dias_anticipacion": 0}
|
||||
|
||||
start_day, end_day, month_str = match.groups()
|
||||
start_day, end_day = int(start_day), int(end_day)
|
||||
month = meses.get(month_str.lower())
|
||||
|
||||
if not month:
|
||||
return {"dias_totales": 0, "dias_anticipacion": 0}
|
||||
|
||||
def _fmt_fecha(fecha_iso: str) -> str:
|
||||
if not fecha_iso:
|
||||
return "N/A"
|
||||
try:
|
||||
start_date = date(current_year, month, start_day)
|
||||
# Si la fecha ya pasó este año, asumir que es del próximo año
|
||||
if start_date < today:
|
||||
start_date = date(current_year + 1, month, start_day)
|
||||
|
||||
end_date = date(start_date.year, month, end_day)
|
||||
|
||||
dias_totales = (end_date - start_date).days + 1
|
||||
dias_anticipacion = (start_date - today).days
|
||||
|
||||
return {"dias_totales": dias_totales, "dias_anticipacion": dias_anticipacion, "fechas_calculadas": {"inicio": start_date.isoformat(), "fin": end_date.isoformat()}}
|
||||
except ValueError:
|
||||
return {"dias_totales": 0, "dias_anticipacion": 0}
|
||||
|
||||
return fecha_iso.split("T")[0]
|
||||
except Exception:
|
||||
return fecha_iso
|
||||
|
||||
# --- Vacaciones ---
|
||||
async def start_vacaciones(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
user = update.effective_user
|
||||
log_request(user.id, user.username, "vacaciones", update.message.text)
|
||||
context.user_data.clear()
|
||||
context.user_data['tipo'] = 'VACACIONES'
|
||||
context.user_data["anio"] = ANIO_ACTUAL
|
||||
await update.message.reply_text(
|
||||
"🌴 **Solicitud de Vacaciones**\n\n¿Para qué fechas las necesitas?\nUsa el formato: `10 al 15 de Octubre`.",
|
||||
reply_markup=TECLADO_MESES,
|
||||
"🌴 **Solicitud de Vacaciones**\n\nUsaré el año actual. ¿En qué *día* inicia tu descanso? (número, ej: 10)",
|
||||
reply_markup=ReplyKeyboardRemove(),
|
||||
)
|
||||
return FECHAS
|
||||
return INICIO_DIA
|
||||
|
||||
# --- Permiso ---
|
||||
async def start_permiso(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
user = update.effective_user
|
||||
log_request(user.id, user.username, "permiso", update.message.text)
|
||||
context.user_data.clear()
|
||||
context.user_data['tipo'] = 'PERMISO'
|
||||
await update.message.reply_text(
|
||||
"⏱️ **Solicitud de Permiso**\n\nSelecciona o escribe el día y horario. Ej: `Jueves 15 09:00-11:00`.",
|
||||
reply_markup=TECLADO_PERMISO_RAPIDO,
|
||||
"⏱️ **Solicitud de Permiso**\n\n¿Para cuándo lo necesitas?",
|
||||
reply_markup=TECLADO_PERMISO_CUANDO,
|
||||
)
|
||||
return FECHAS
|
||||
return PERMISO_CUANDO
|
||||
|
||||
async def recibir_fechas(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
texto_fechas = update.message.text
|
||||
es_vacaciones = context.user_data.get('tipo') == 'VACACIONES'
|
||||
# --- Selección de año / cuando ---
|
||||
async def recibir_anio_vacaciones(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
anio = _parse_anio(update.message.text)
|
||||
if anio not in (ANIO_ACTUAL, ANIO_ACTUAL + 1):
|
||||
await update.message.reply_text("Elige el año del teclado (actual o siguiente).", reply_markup=TECLADO_ANIOS)
|
||||
return VAC_ANIO
|
||||
context.user_data["anio"] = anio
|
||||
await update.message.reply_text("¿Qué *día* termina?", reply_markup=ReplyKeyboardRemove())
|
||||
return FIN_DIA
|
||||
|
||||
if es_vacaciones:
|
||||
metrics = _calculate_vacation_metrics(texto_fechas)
|
||||
if metrics["dias_totales"] == 0:
|
||||
async def recibir_cuando_permiso(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
texto = update.message.text.strip().lower()
|
||||
hoy = date.today()
|
||||
offset_map = {"hoy": 0, "mañana": 1, "manana": 1, "pasado mañana": 2, "pasado manana": 2}
|
||||
if texto in offset_map:
|
||||
delta = offset_map[texto]
|
||||
fecha = hoy.fromordinal(hoy.toordinal() + delta)
|
||||
context.user_data["anio"] = fecha.year
|
||||
context.user_data["inicio_dia"] = fecha.day
|
||||
context.user_data["inicio_mes"] = fecha.month
|
||||
context.user_data["fin_dia"] = fecha.day
|
||||
context.user_data["fin_mes"] = fecha.month
|
||||
await update.message.reply_text("¿Cuál es el horario? Ej: `09:00-11:00` o `Todo el día`.", reply_markup=ReplyKeyboardRemove())
|
||||
return HORARIO
|
||||
if "fecha" in texto:
|
||||
context.user_data["anio"] = ANIO_ACTUAL
|
||||
await update.message.reply_text("¿En qué *día* inicia el permiso? (número, ej: 12)", reply_markup=ReplyKeyboardRemove())
|
||||
return INICIO_DIA
|
||||
await update.message.reply_text("Elige una opción: Hoy, Mañana, Pasado mañana o Fecha específica.", reply_markup=TECLADO_PERMISO_CUANDO)
|
||||
return PERMISO_CUANDO
|
||||
|
||||
async def recibir_anio_permiso(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
anio = _parse_anio(update.message.text)
|
||||
if anio not in (ANIO_ACTUAL, ANIO_ACTUAL + 1):
|
||||
await update.message.reply_text("Elige el año del teclado (actual o siguiente).", reply_markup=TECLADO_ANIOS)
|
||||
return PERMISO_ANIO
|
||||
context.user_data["anio"] = anio
|
||||
await update.message.reply_text("¿Qué *día* termina?", reply_markup=ReplyKeyboardRemove())
|
||||
return FIN_DIA
|
||||
|
||||
# --- Captura de fechas ---
|
||||
async def recibir_inicio_dia(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
dia = _parse_dia(update.message.text)
|
||||
if not dia:
|
||||
await update.message.reply_text("Necesito un número de día válido (1-31). Intenta de nuevo.")
|
||||
return INICIO_DIA
|
||||
context.user_data["inicio_dia"] = dia
|
||||
await update.message.reply_text("¿De qué *mes* inicia?", reply_markup=TECLADO_MESES)
|
||||
return INICIO_MES
|
||||
|
||||
async def recibir_inicio_mes(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
mes = _parse_mes(update.message.text)
|
||||
if not mes:
|
||||
await update.message.reply_text("Elige un mes del teclado o escríbelo igual que aparece.", reply_markup=TECLADO_MESES)
|
||||
return INICIO_MES
|
||||
context.user_data["inicio_mes"] = mes
|
||||
context.user_data.setdefault("anio", ANIO_ACTUAL)
|
||||
|
||||
try:
|
||||
inicio_candidato = date(context.user_data["anio"], mes, context.user_data["inicio_dia"])
|
||||
if inicio_candidato < date.today():
|
||||
await update.message.reply_text(
|
||||
"No entendí las fechas. Usa un formato como `10 al 15 de Octubre`.",
|
||||
reply_markup=TECLADO_MESES,
|
||||
"Esa fecha ya pasó este año. ¿Para qué año la agendamos?",
|
||||
reply_markup=TECLADO_ANIOS
|
||||
)
|
||||
return FECHAS
|
||||
# Si se entiende, guardamos también las métricas preliminares
|
||||
context.user_data['metricas_preliminares'] = metrics
|
||||
return VAC_ANIO if context.user_data.get("tipo") == "VACACIONES" else PERMISO_ANIO
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
await update.message.reply_text("¿Qué *día* termina?", reply_markup=ReplyKeyboardRemove())
|
||||
return FIN_DIA
|
||||
|
||||
async def recibir_fin_dia(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
dia = _parse_dia(update.message.text)
|
||||
if not dia:
|
||||
await update.message.reply_text("Día inválido. Dame un número de 1 a 31.")
|
||||
return FIN_DIA
|
||||
context.user_data["fin_dia"] = dia
|
||||
await update.message.reply_text("¿De qué *mes* termina?", reply_markup=TECLADO_MESES)
|
||||
return FIN_MES
|
||||
|
||||
async def recibir_fin_mes(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
mes = _parse_mes(update.message.text)
|
||||
if not mes:
|
||||
await update.message.reply_text("Elige un mes válido.", reply_markup=TECLADO_MESES)
|
||||
return FIN_MES
|
||||
context.user_data["fin_mes"] = mes
|
||||
|
||||
if context.user_data.get("tipo") == "PERMISO":
|
||||
await update.message.reply_text("¿Cuál es el horario? Ej: `09:00-11:00` o `Todo el día`.", reply_markup=ReplyKeyboardRemove())
|
||||
return HORARIO
|
||||
|
||||
context.user_data['fechas'] = texto_fechas
|
||||
await update.message.reply_text("Entendido. ¿Cuál es el motivo o comentario adicional?", reply_markup=ReplyKeyboardRemove())
|
||||
return MOTIVO
|
||||
|
||||
async def recibir_horario(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
context.user_data["horario"] = update.message.text.strip()
|
||||
await update.message.reply_text("Entendido. ¿Cuál es el motivo o comentario adicional?", reply_markup=ReplyKeyboardRemove())
|
||||
return MOTIVO
|
||||
|
||||
# --- Motivo y cierre ---
|
||||
async def recibir_motivo_fin(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
motivo = update.message.text
|
||||
datos = context.user_data
|
||||
user = update.effective_user
|
||||
|
||||
fechas = _build_dates(datos)
|
||||
if not fechas:
|
||||
await update.message.reply_text("🤔 No entendí las fechas. Por favor, inicia otra vez con /vacaciones o /permiso.")
|
||||
return ConversationHandler.END
|
||||
|
||||
# Generar payload base
|
||||
payload = {
|
||||
"record_id": str(uuid.uuid4()),
|
||||
"solicitante": {
|
||||
@@ -131,7 +249,10 @@ async def recibir_motivo_fin(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
||||
"username": user.username
|
||||
},
|
||||
"tipo_solicitud": datos['tipo'],
|
||||
"fechas_texto_original": datos['fechas'],
|
||||
"fechas": {
|
||||
"inicio": fechas.get("inicio").isoformat() if fechas else None,
|
||||
"fin": fechas.get("fin").isoformat() if fechas else None,
|
||||
},
|
||||
"motivo_usuario": motivo,
|
||||
"created_at": datetime.now().isoformat()
|
||||
}
|
||||
@@ -141,11 +262,12 @@ async def recibir_motivo_fin(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
||||
webhooks = _get_webhook_list("WEBHOOK_PERMISOS")
|
||||
categoria = classify_reason(motivo)
|
||||
payload["categoria_detectada"] = categoria
|
||||
payload["horario"] = datos.get("horario", "N/A")
|
||||
await update.message.reply_text(f"Categoría detectada → **{categoria}** 🚨")
|
||||
|
||||
elif datos['tipo'] == 'VACACIONES':
|
||||
webhooks = _get_webhook_list("WEBHOOK_VACACIONES")
|
||||
metrics = datos.get('metricas_preliminares') or _calculate_vacation_metrics(datos['fechas'])
|
||||
metrics = _calculate_vacation_metrics_from_dates(fechas)
|
||||
|
||||
if metrics["dias_totales"] > 0:
|
||||
payload["metricas"] = metrics
|
||||
@@ -154,27 +276,49 @@ async def recibir_motivo_fin(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
||||
if dias <= 5:
|
||||
status = "RECHAZADO"
|
||||
mensaje = f"🔴 {dias} días es un periodo muy corto. Las vacaciones deben ser de al menos 6 días."
|
||||
elif dias > 30:
|
||||
status = "RECHAZADO"
|
||||
mensaje = "🔴 Las vacaciones no pueden exceder 30 días. Ajusta tus fechas, por favor."
|
||||
elif 6 <= dias <= 11:
|
||||
status = "REVISION_MANUAL"
|
||||
mensaje = f"🟡 Solicitud de {dias} días recibida. Tu manager la revisará pronto."
|
||||
else: # 12+
|
||||
else: # 12-30
|
||||
status = "PRE_APROBADO"
|
||||
mensaje = f"🟢 ¡Excelente planeación! Tu solicitud de {dias} días ha sido pre-aprobada."
|
||||
mensaje = f"🟢 ¡Excelente planeación! Tu solicitud de {dias} días ha sido pre-aprobada (ideal: 12 días)."
|
||||
|
||||
payload["status_inicial"] = status
|
||||
await update.message.reply_text(mensaje)
|
||||
else:
|
||||
# Si no se pudieron parsear las fechas
|
||||
payload["status_inicial"] = "ERROR_FECHAS"
|
||||
await update.message.reply_text("🤔 No entendí las fechas. Por favor, usa un formato como '10 al 15 de Octubre'.")
|
||||
await update.message.reply_text("🤔 No entendí las fechas. Por favor, comparte día y mes otra vez con /vacaciones.")
|
||||
|
||||
try:
|
||||
enviados = _send_webhooks(webhooks, payload) if webhooks else 0
|
||||
tipo_solicitud_texto = "Permiso" if datos['tipo'] == 'PERMISO' else 'Vacaciones'
|
||||
if enviados > 0:
|
||||
await update.message.reply_text(f"✅ Solicitud de *{tipo_solicitud_texto}* enviada a tu Manager.")
|
||||
inicio_txt = _fmt_fecha(payload["fechas"]["inicio"])
|
||||
fin_txt = _fmt_fecha(payload["fechas"]["fin"])
|
||||
if datos['tipo'] == 'PERMISO':
|
||||
resumen = (
|
||||
"📝 Resumen enviado:\n"
|
||||
f"- Fecha: {inicio_txt} a {fin_txt}\n"
|
||||
f"- Horario: {payload.get('horario', 'N/A')}\n"
|
||||
f"- Categoría: {payload.get('categoria_detectada', 'N/A')}\n"
|
||||
f"- Motivo: {motivo}"
|
||||
)
|
||||
else:
|
||||
await update.message.reply_text("⚠️ No hay webhook configurado o falló el envío. RH lo revisará.")
|
||||
m = payload.get("metricas", {})
|
||||
resumen = (
|
||||
"📝 Resumen enviado:\n"
|
||||
f"- Inicio: {inicio_txt}\n"
|
||||
f"- Fin: {fin_txt}\n"
|
||||
f"- Días totales: {m.get('dias_totales', 'N/A')}\n"
|
||||
f"- Anticipación: {m.get('dias_anticipacion', 'N/A')} días\n"
|
||||
f"- Estatus inicial: {payload.get('status_inicial', 'N/A')}"
|
||||
)
|
||||
if enviados > 0:
|
||||
await update.message.reply_text(f"✅ Solicitud de *{tipo_solicitud_texto}* enviada a tu Manager.\n\n{resumen}")
|
||||
else:
|
||||
await update.message.reply_text(f"⚠️ No hay webhook configurado o falló el envío. RH lo revisará.\n\n{resumen}")
|
||||
except Exception as e:
|
||||
print(f"Error enviando webhook: {e}")
|
||||
await update.message.reply_text("⚠️ Error enviando la solicitud.")
|
||||
@@ -190,7 +334,11 @@ async def cancelar(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||
vacaciones_handler = ConversationHandler(
|
||||
entry_points=[CommandHandler("vacaciones", start_vacaciones)],
|
||||
states={
|
||||
FECHAS: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_fechas)],
|
||||
VAC_ANIO: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_anio_vacaciones)],
|
||||
INICIO_DIA: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_inicio_dia)],
|
||||
INICIO_MES: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_inicio_mes)],
|
||||
FIN_DIA: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_fin_dia)],
|
||||
FIN_MES: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_fin_mes)],
|
||||
MOTIVO: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_motivo_fin)]
|
||||
},
|
||||
fallbacks=[CommandHandler("cancelar", cancelar)]
|
||||
@@ -199,7 +347,13 @@ vacaciones_handler = ConversationHandler(
|
||||
permiso_handler = ConversationHandler(
|
||||
entry_points=[CommandHandler("permiso", start_permiso)],
|
||||
states={
|
||||
FECHAS: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_fechas)],
|
||||
PERMISO_CUANDO: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_cuando_permiso)],
|
||||
PERMISO_ANIO: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_anio_permiso)],
|
||||
INICIO_DIA: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_inicio_dia)],
|
||||
INICIO_MES: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_inicio_mes)],
|
||||
FIN_DIA: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_fin_dia)],
|
||||
FIN_MES: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_fin_mes)],
|
||||
HORARIO: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_horario)],
|
||||
MOTIVO: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_motivo_fin)]
|
||||
},
|
||||
fallbacks=[CommandHandler("cancelar", cancelar)]
|
||||
|
||||
Reference in New Issue
Block a user