Changed markdown to html
This commit is contained in:
@@ -92,11 +92,11 @@ async def check_expired_reservations(context: ContextTypes.DEFAULT_TYPE):
|
|||||||
cancelled_count += 1
|
cancelled_count += 1
|
||||||
# Try to notify the user using context.bot
|
# Try to notify the user using context.bot
|
||||||
notification_text = (
|
notification_text = (
|
||||||
f"Las participaciones `{numbers}` que tenías reservadas para el sorteo **{raffle_name}** han sido liberadas.\n\n"
|
f"Las participaciones <code>{numbers}</code> que tenías reservadas para el sorteo <b>{raffle_name}</b> han sido liberadas.\n\n"
|
||||||
f"Puedes volver a reservarlas, ¡pero tienes {RESERVATION_TIMEOUT_MINUTES} minutos para completar el pago!."
|
f"Puedes volver a reservarlas, ¡pero tienes {RESERVATION_TIMEOUT_MINUTES} minutos para completar el pago!."
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await context.bot.send_message(chat_id=user_id, text=notification_text, parse_mode=ParseMode.MARKDOWN)
|
await context.bot.send_message(chat_id=user_id, text=notification_text, parse_mode=ParseMode.HTML)
|
||||||
logger.info(f"Notified user {user_id} (Name: {reservation['user_name']}) about expired reservation.")
|
logger.info(f"Notified user {user_id} (Name: {reservation['user_name']}) about expired reservation.")
|
||||||
except Forbidden:
|
except Forbidden:
|
||||||
logger.warning(f"Cannot notify user {user_id} (Forbidden). Reservation cancelled anyway.")
|
logger.warning(f"Cannot notify user {user_id} (Forbidden). Reservation cancelled anyway.")
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ logger = logging.getLogger(__name__)
|
|||||||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
user = update.message.from_user
|
user = update.message.from_user
|
||||||
args = context.args
|
args = context.args
|
||||||
|
if not user.username:
|
||||||
|
await update.message.reply_text("Por favor, configura un nombre de usuario en Telegram para participar. ¡Es necesario para notificarte si ganas!")
|
||||||
|
return
|
||||||
if args and args[0].startswith("join_"):
|
if args and args[0].startswith("join_"):
|
||||||
try:
|
try:
|
||||||
raffle_id = int(args[0].split("_")[1])
|
raffle_id = int(args[0].split("_")[1])
|
||||||
@@ -75,9 +78,9 @@ async def new_raffle_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -
|
|||||||
keyboard = generate_channel_selection_keyboard()
|
keyboard = generate_channel_selection_keyboard()
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
"Vamos a crear un nuevo sorteo.\n\n"
|
"Vamos a crear un nuevo sorteo.\n\n"
|
||||||
"**Paso 1:** Selecciona el canal donde se publicará el sorteo.",
|
"<b>Paso 1:</b> Selecciona el canal donde se publicará el sorteo.",
|
||||||
reply_markup=keyboard,
|
reply_markup=keyboard,
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.HTML
|
||||||
)
|
)
|
||||||
return SELECTING_CHANNEL
|
return SELECTING_CHANNEL
|
||||||
|
|
||||||
@@ -93,8 +96,8 @@ async def select_channel(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
context.user_data['new_raffle']['channel'] = channel_id
|
context.user_data['new_raffle']['channel'] = channel_id
|
||||||
|
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
"Canal seleccionad. Ahora, por favor, envía el **título** del sorteo.",
|
"Canal seleccionad. Ahora, por favor, envía el <b>título</b> del sorteo.",
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.HTML
|
||||||
)
|
)
|
||||||
return TYPING_TITLE
|
return TYPING_TITLE
|
||||||
|
|
||||||
@@ -110,7 +113,7 @@ async def receive_title(update: Update, context: ContextTypes.DEFAULT_TYPE) -> i
|
|||||||
return TYPING_TITLE
|
return TYPING_TITLE
|
||||||
|
|
||||||
context.user_data['new_raffle']['title'] = title
|
context.user_data['new_raffle']['title'] = title
|
||||||
await update.message.reply_text("Título guardado. Ahora envía la **descripción** del sorteo.", parse_mode=ParseMode.MARKDOWN)
|
await update.message.reply_text("Título guardado. Ahora envía la <b>descripción</b> del sorteo.", parse_mode=ParseMode.HTML)
|
||||||
return TYPING_DESCRIPTION
|
return TYPING_DESCRIPTION
|
||||||
|
|
||||||
async def receive_description(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
async def receive_description(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||||
@@ -121,7 +124,7 @@ async def receive_description(update: Update, context: ContextTypes.DEFAULT_TYPE
|
|||||||
return TYPING_DESCRIPTION
|
return TYPING_DESCRIPTION
|
||||||
|
|
||||||
context.user_data['new_raffle']['description'] = description
|
context.user_data['new_raffle']['description'] = description
|
||||||
await update.message.reply_text("Descripción guardada. Ahora envía la **imagen** para el sorteo.", parse_mode=ParseMode.MARKDOWN)
|
await update.message.reply_text("Descripción guardada. Ahora envía la <b>imagen</b> para el sorteo.", parse_mode=ParseMode.HTML)
|
||||||
return SENDING_IMAGE
|
return SENDING_IMAGE
|
||||||
|
|
||||||
async def receive_image(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
async def receive_image(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||||
@@ -135,7 +138,7 @@ async def receive_image(update: Update, context: ContextTypes.DEFAULT_TYPE) -> i
|
|||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
"Imagen guardada. Ahora, ¿el sorteo permite envíos internacionales? Responde con 'Sí' o 'No'.",
|
"Imagen guardada. Ahora, ¿el sorteo permite envíos internacionales? Responde con 'Sí' o 'No'.",
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.HTML
|
||||||
)
|
)
|
||||||
return INTERNATIONAL_SHIPPING
|
return INTERNATIONAL_SHIPPING
|
||||||
|
|
||||||
@@ -152,7 +155,7 @@ async def receive_international_shipping(update: Update, context: ContextTypes.D
|
|||||||
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
"Perfecto. Ahora, introduce el precio por número para el canal seleccionado (solo el número, ej: 5).",
|
"Perfecto. Ahora, introduce el precio por número para el canal seleccionado (solo el número, ej: 5).",
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.HTML
|
||||||
)
|
)
|
||||||
return TYPING_PRICE_FOR_CHANNEL
|
return TYPING_PRICE_FOR_CHANNEL
|
||||||
|
|
||||||
@@ -183,13 +186,13 @@ async def _show_creation_confirmation(update: Update, context: ContextTypes.DEFA
|
|||||||
price = raffle_data.get('price', 0)
|
price = raffle_data.get('price', 0)
|
||||||
|
|
||||||
confirmation_text = (
|
confirmation_text = (
|
||||||
"¡Perfecto! Revisa los datos del sortoe:\n\n"
|
"¡Perfecto! Revisa los datos del sorteo:\n\n"
|
||||||
f"📌 **Título:** {raffle_data.get('title', 'N/A')}\n"
|
f"📌 <b>Título:</b> {raffle_data.get('title', 'N/A')}\n"
|
||||||
f"📝 **Descripción:** {raffle_data.get('description', 'N/A')}\n"
|
f"📝 <b>Descripción:</b> {raffle_data.get('description', 'N/A')}\n"
|
||||||
f"📺 **Canal:** {REVERSE_CHANNELS.get(channel_id, channel_id)}\n"
|
f"📺 <b>Canal:</b> {REVERSE_CHANNELS.get(channel_id, channel_id)}\n"
|
||||||
f"🌍 **Envío internacional:** {'Sí ✅' if raffle_data.get('international_shipping', 0) else 'No ❌'}\n"
|
f"🌍 <b>Envío internacional:</b> {'Sí ✅' if raffle_data.get('international_shipping', 0) else 'No ❌'}\n"
|
||||||
f"💶 **Donación mínima:** {price}€\n"
|
f"💶 <b>Donación mínima:</b> {price}€\n"
|
||||||
f"🖼️ **Imagen:** (Adjunta)\n\n"
|
f"🖼️ <b>Imagen:</b> (Adjunta)\n\n"
|
||||||
"¿Confirmas la creación de este sorteo?"
|
"¿Confirmas la creación de este sorteo?"
|
||||||
)
|
)
|
||||||
keyboard = generate_confirmation_keyboard()
|
keyboard = generate_confirmation_keyboard()
|
||||||
@@ -200,7 +203,7 @@ async def _show_creation_confirmation(update: Update, context: ContextTypes.DEFA
|
|||||||
photo=raffle_data['image_file_id'],
|
photo=raffle_data['image_file_id'],
|
||||||
caption=confirmation_text,
|
caption=confirmation_text,
|
||||||
reply_markup=keyboard,
|
reply_markup=keyboard,
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.HTML
|
||||||
)
|
)
|
||||||
|
|
||||||
async def confirm_creation(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
async def confirm_creation(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||||
@@ -330,7 +333,11 @@ async def number_callback(update: Update, context: CallbackContext):
|
|||||||
"""Handles clicks on the number buttons in the private chat."""
|
"""Handles clicks on the number buttons in the private chat."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
user_id = query.from_user.id
|
user_id = query.from_user.id
|
||||||
username = query.from_user.username or query.from_user.first_name
|
|
||||||
|
if not query.from_user.username:
|
||||||
|
await query.answer("Por favor, configura un nombre de usuario en Telegram para participar.")
|
||||||
|
return
|
||||||
|
username = query.from_user.username
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = query.data.split(':')
|
data = query.data.split(':')
|
||||||
@@ -642,9 +649,9 @@ async def end_raffle_logic(context: ContextTypes.DEFAULT_TYPE, raffle_id: int, w
|
|||||||
|
|
||||||
winners_str = get_winners(raffle_id, winner_numbers)
|
winners_str = get_winners(raffle_id, winner_numbers)
|
||||||
formatted_winner_numbers = ", ".join(f"{n:02}" for n in sorted(winner_numbers))
|
formatted_winner_numbers = ", ".join(f"{n:02}" for n in sorted(winner_numbers))
|
||||||
announcement = f"🎯🏆🎯 **¡Resultados del Sorteo '{raffle_name}'!** 🎯🏆🎯\n\n"
|
announcement = f"🎯🏆🎯 <b>¡Resultados del Sorteo '{raffle_name}'!</b> 🎯🏆🎯\n\n"
|
||||||
announcement += f"Detalles del sorteo: https://t.me/{channel_alias}/{get_main_message_id(raffle_id)}\n"
|
announcement += f"Detalles del sorteo: https://t.me/{channel_alias}/{get_main_message_id(raffle_id)}\n"
|
||||||
announcement += f"Participaciones ganadoras: **{formatted_winner_numbers}**\n\n" if len(winner_numbers) > 1 else f"Participación ganadora: **{formatted_winner_numbers}**\n\n"
|
announcement += f"Participaciones ganadoras: <b>{formatted_winner_numbers}</b>\n\n" if len(winner_numbers) > 1 else f"Participación ganadora: <b>{formatted_winner_numbers}</b>\n\n"
|
||||||
if winners_str: # Ensure winners_str is not empty or a "no winners" message itself
|
if winners_str: # Ensure winners_str is not empty or a "no winners" message itself
|
||||||
announcement += f"Ganadores:\n{winners_str}\n\n¡Felicidades!" if len(winner_numbers) > 1 else f"Ganador:\n{winners_str}\n\n¡Felicidades!"
|
announcement += f"Ganadores:\n{winners_str}\n\n¡Felicidades!" if len(winner_numbers) > 1 else f"Ganador:\n{winners_str}\n\n¡Felicidades!"
|
||||||
else:
|
else:
|
||||||
@@ -652,16 +659,16 @@ async def end_raffle_logic(context: ContextTypes.DEFAULT_TYPE, raffle_id: int, w
|
|||||||
announcement += f"\nPuedes comprobar los resultados en {JUEGOS_ONCE_URL}"
|
announcement += f"\nPuedes comprobar los resultados en {JUEGOS_ONCE_URL}"
|
||||||
announcement += "\n\nGracias a todos por participar. Mantente atento a futuros sorteos."
|
announcement += "\n\nGracias a todos por participar. Mantente atento a futuros sorteos."
|
||||||
|
|
||||||
main_announcement = f"🎯🏆🎯 **Sorteo '{raffle_name}' terminado** 🎯🏆🎯\n\n"
|
main_announcement = f"🎯🏆🎯 <b>Sorteo '{raffle_name}' terminado</b> 🎯🏆🎯\n\n"
|
||||||
main_announcement += f"{raffle_details['description']}\n\n"
|
main_announcement += f"{raffle_details['description']}\n\n"
|
||||||
main_announcement += f"🌍 **Envío internacional:** {'Sí ✅' if raffle_details['international_shipping'] else 'No ❌'}\n"
|
main_announcement += f"🌍 <b>Envío internacional:</b> {'Sí ✅' if raffle_details['international_shipping'] else 'No ❌'}\n"
|
||||||
main_announcement += f"💵 **Donación mínima:** {raffle_details['price']}€\n"
|
main_announcement += f"💵 <b>Donación mínima:</b> {raffle_details['price']}€\n"
|
||||||
main_announcement += f"📜 Normas y condiciones: {TYC_DOCUMENT_URL}"
|
main_announcement += f"📜 <b>Normas y condiciones:</b> {TYC_DOCUMENT_URL}"
|
||||||
|
|
||||||
main_message_id = get_main_message_id(raffle_id)
|
main_message_id = get_main_message_id(raffle_id)
|
||||||
try:
|
try:
|
||||||
await context.bot.send_message(chat_id=int(channel_id_str), text=announcement, parse_mode=ParseMode.MARKDOWN)
|
await context.bot.send_message(chat_id=int(channel_id_str), text=announcement, parse_mode=ParseMode.HTML)
|
||||||
await context.bot.edit_message_caption(chat_id=int(channel_id_str), message_id=main_message_id, caption=main_announcement, reply_markup=None, parse_mode=ParseMode.MARKDOWN)
|
await context.bot.edit_message_caption(chat_id=int(channel_id_str), message_id=main_message_id, caption=main_announcement, reply_markup=None, parse_mode=ParseMode.HTML)
|
||||||
logger.info(f"Announced winners for raffle {raffle_id} in channel {channel_alias} (ID: {channel_id_str})")
|
logger.info(f"Announced winners for raffle {raffle_id} in channel {channel_alias} (ID: {channel_id_str})")
|
||||||
except Forbidden:
|
except Forbidden:
|
||||||
logger.error(f"Permission error announcing winners in channel {channel_alias} (ID: {channel_id_str}).")
|
logger.error(f"Permission error announcing winners in channel {channel_alias} (ID: {channel_id_str}).")
|
||||||
@@ -719,7 +726,7 @@ async def admin_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
logger.info(f"Admin {user.id} accessed /menu")
|
logger.info(f"Admin {user.id} accessed /menu")
|
||||||
keyboard = generate_admin_main_menu_keyboard()
|
keyboard = generate_admin_main_menu_keyboard()
|
||||||
await update.message.reply_text("🛠️ **Menú de Administrador** 🛠️", reply_markup=keyboard, parse_mode=ParseMode.MARKDOWN)
|
await update.message.reply_text("🛠️ <b>Menú de Administrador</b> 🛠️", reply_markup=keyboard, parse_mode=ParseMode.HTML)
|
||||||
|
|
||||||
async def admin_menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def admin_menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Handles callbacks from the admin menus."""
|
"""Handles callbacks from the admin menus."""
|
||||||
@@ -747,12 +754,12 @@ async def admin_menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE
|
|||||||
logger.info(f"Admin {user_id} requested raffle list.")
|
logger.info(f"Admin {user_id} requested raffle list.")
|
||||||
keyboard = generate_admin_list_raffles_keyboard()
|
keyboard = generate_admin_list_raffles_keyboard()
|
||||||
active_raffles = get_active_raffles()
|
active_raffles = get_active_raffles()
|
||||||
message_text = "**Sorteos Activos**\n\nSelecciona un sorteo para ver detalles, anunciar o terminar:" if active_raffles else "**Sorteos Activas**\n\nNo hay sorteos activos."
|
message_text = "<b>Sorteos Activos</b>\n\nSelecciona un sorteo para ver detalles, anunciar o terminar:" if active_raffles else "<b>Sorteos Activas</b>\n\nNo hay sorteos activos."
|
||||||
await query.edit_message_text(message_text, reply_markup=keyboard, parse_mode=ParseMode.MARKDOWN)
|
await query.edit_message_text(message_text, reply_markup=keyboard, parse_mode=ParseMode.HTML)
|
||||||
|
|
||||||
elif data == ADMIN_MENU_BACK_MAIN:
|
elif data == ADMIN_MENU_BACK_MAIN:
|
||||||
keyboard = generate_admin_main_menu_keyboard()
|
keyboard = generate_admin_main_menu_keyboard()
|
||||||
await query.edit_message_text("🛠️ **Menú de Administrador** 🛠️", reply_markup=keyboard, parse_mode=ParseMode.MARKDOWN)
|
await query.edit_message_text("🛠️ <b>Menú de Administrador</b> 🛠️", reply_markup=keyboard, parse_mode=ParseMode.HTML)
|
||||||
|
|
||||||
# --- Raffle Specific Actions ---
|
# --- Raffle Specific Actions ---
|
||||||
elif data.startswith(ADMIN_VIEW_RAFFLE_PREFIX):
|
elif data.startswith(ADMIN_VIEW_RAFFLE_PREFIX):
|
||||||
@@ -800,10 +807,10 @@ async def admin_menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE
|
|||||||
|
|
||||||
keyboard = generate_admin_cancel_end_keyboard()
|
keyboard = generate_admin_cancel_end_keyboard()
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
f"Vas a terminar el sorteo: **{raffle['name']}**\n\n"
|
f"Vas a terminar el sorteo: <b>{raffle['name']}</b>\n\n"
|
||||||
"Por favor, envía ahora las **participaciones ganadoras** separadas por espacios (ej: `7 23 81`).",
|
"Por favor, envía ahora las <b>participaciones ganadoras</b> separadas por espacios (ej: <code>7 23 81</code>).",
|
||||||
reply_markup=keyboard,
|
reply_markup=keyboard,
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.HTML
|
||||||
)
|
)
|
||||||
elif data == ADMIN_CANCEL_END_PROCESS:
|
elif data == ADMIN_CANCEL_END_PROCESS:
|
||||||
# Clear the flags
|
# Clear the flags
|
||||||
@@ -812,7 +819,7 @@ async def admin_menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE
|
|||||||
logger.info(f"Admin {user_id} cancelled the raffle end process.")
|
logger.info(f"Admin {user_id} cancelled the raffle end process.")
|
||||||
# Go back to the raffle list
|
# Go back to the raffle list
|
||||||
keyboard = generate_admin_list_raffles_keyboard()
|
keyboard = generate_admin_list_raffles_keyboard()
|
||||||
await query.edit_message_text("**Sorteos Activos**\n\nSelecciona un sorteo para terminarlo:", reply_markup=keyboard, parse_mode=ParseMode.MARKDOWN)
|
await query.edit_message_text("<b>Sorteos Activos</b>\n\nSelecciona un sorteo para terminarlo:", reply_markup=keyboard, parse_mode=ParseMode.HTML)
|
||||||
|
|
||||||
elif data == ADMIN_NO_OP:
|
elif data == ADMIN_NO_OP:
|
||||||
# Just ignore clicks on placeholder buttons like "No hay sorteos activos"
|
# Just ignore clicks on placeholder buttons like "No hay sorteos activos"
|
||||||
@@ -854,9 +861,9 @@ async def admin_receive_winner_numbers(update: Update, context: ContextTypes.DEF
|
|||||||
keyboard = generate_admin_cancel_end_keyboard()
|
keyboard = generate_admin_cancel_end_keyboard()
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
f"❌ Participaciones inválidas: {e}\n\n"
|
f"❌ Participaciones inválidas: {e}\n\n"
|
||||||
"Por favor, envía las participaciones ganadoras (0-99) separadas por espacios (ej: `7 23 81`).",
|
"Por favor, envía las participaciones ganadoras (0-99) separadas por espacios (ej: <code>7 23 81</code>).",
|
||||||
reply_markup=keyboard,
|
reply_markup=keyboard,
|
||||||
parse_mode=ParseMode.MARKDOWN
|
parse_mode=ParseMode.HTML
|
||||||
)
|
)
|
||||||
# Keep expecting input
|
# Keep expecting input
|
||||||
return
|
return
|
||||||
@@ -906,16 +913,16 @@ async def _announce_raffle_in_channels(context: ContextTypes.DEFAULT_TYPE, raffl
|
|||||||
channel_alias = REVERSE_CHANNELS.get(channel_id_str, f"ID:{channel_id_str}")
|
channel_alias = REVERSE_CHANNELS.get(channel_id_str, f"ID:{channel_id_str}")
|
||||||
|
|
||||||
announce_caption = (
|
announce_caption = (
|
||||||
f"🏆 **¡{'Nuevo ' if initial_announcement else ''}Sorteo Disponible!** 🏆\n\n"
|
f"🏆 <b>¡{'Nuevo ' if initial_announcement else ''}Sorteo Disponible!</b> 🏆\n\n"
|
||||||
f"🌟 **{raffle_name}** 🌟\n\n"
|
f"🌟 <b>{raffle_name}</b> 🌟\n\n"
|
||||||
f"{raffle_description}\n\n"
|
f"{raffle_description}\n\n"
|
||||||
f"🌍 **Envío internacional:** {'Sí ✅' if raffle['international_shipping'] else 'No ❌'}\n"
|
f"🌍 <b>Envío internacional:</b> {'Sí ✅' if raffle['international_shipping'] else 'No ❌'}\n"
|
||||||
f"💵 **Donación mínima:** {price}€\n"
|
f"💵 <b>Donación mínima:</b> {price}€\n"
|
||||||
f"🎟️ **Participaciones disponibles:** {remaining_count if remaining_count >= 0 else 'N/A'}\n\n"
|
f"🎟️ <b>Participaciones disponibles:</b> {remaining_count if remaining_count >= 0 else 'N/A'}\n\n"
|
||||||
f"📜 Normas y condiciones: {TYC_DOCUMENT_URL}"
|
f"📜 Normas y condiciones: {TYC_DOCUMENT_URL}"
|
||||||
)
|
)
|
||||||
|
|
||||||
message_args = {"parse_mode": ParseMode.MARKDOWN}
|
message_args = {"parse_mode": ParseMode.HTML}
|
||||||
if image_file_id:
|
if image_file_id:
|
||||||
message_args["photo"] = image_file_id
|
message_args["photo"] = image_file_id
|
||||||
message_args["caption"] = announce_caption
|
message_args["caption"] = announce_caption
|
||||||
@@ -960,7 +967,7 @@ async def _announce_raffle_in_channels(context: ContextTypes.DEFAULT_TYPE, raffl
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
msg_to_admin = "Anuncio enviado con éxito."
|
msg_to_admin = "Anuncio enviado con éxito."
|
||||||
await context.bot.send_message(admin_user_id, msg_to_admin, parse_mode=ParseMode.MARKDOWN)
|
await context.bot.send_message(admin_user_id, msg_to_admin, parse_mode=ParseMode.HTML)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to send announcement summary to admin {admin_user_id}: {e}")
|
logger.error(f"Failed to send announcement summary to admin {admin_user_id}: {e}")
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ def get_winners(raffle_id, winner_numbers_int):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
user_id = participant['user_id']
|
user_id = participant['user_id']
|
||||||
user_name = escape_markdown_v2_chars_for_username(participant['user_name']) or f"User_{user_id}" # Fallback name
|
user_name = participant['user_name'] or f"User_{user_id}" # Fallback name
|
||||||
numbers_str = participant['numbers']
|
numbers_str = participant['numbers']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -104,7 +104,7 @@ def get_winners(raffle_id, winner_numbers_int):
|
|||||||
for user_name, numbers in winners.items():
|
for user_name, numbers in winners.items():
|
||||||
# Ensure numbers are unique in the final output per user
|
# Ensure numbers are unique in the final output per user
|
||||||
unique_numbers_str = ", ".join(sorted(list(set(numbers))))
|
unique_numbers_str = ", ".join(sorted(list(set(numbers))))
|
||||||
winners_message_parts.append(f"- @{escape_markdown_v2_chars_for_username(user_name)} acertó: **{unique_numbers_str}**")
|
winners_message_parts.append(f"- @{user_name} acertó: <b>{unique_numbers_str}</b>")
|
||||||
|
|
||||||
return "\n".join(winners_message_parts)
|
return "\n".join(winners_message_parts)
|
||||||
|
|
||||||
@@ -371,22 +371,6 @@ def generate_table_image(raffle_id):
|
|||||||
img.save(image_path)
|
img.save(image_path)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def escape_markdown_v2_chars_for_username(text: str) -> str:
|
|
||||||
"""Escapes characters for MarkdownV2, specifically for usernames."""
|
|
||||||
# For usernames, usually only _ and * are problematic if not part of actual formatting
|
|
||||||
# Other MarkdownV2 special characters: `[` `]` `(` `)` `~` `>` `#` `+` `-` `=` `|` `{` `}` `.` `!`
|
|
||||||
# We are most concerned with _ in @user_name context.
|
|
||||||
# A more comprehensive list of characters to escape for general text:
|
|
||||||
# escape_chars = r'_*[]()~`>#+-=|{}.!'
|
|
||||||
# For just usernames in this context, focus on what breaks @user_name
|
|
||||||
escape_chars = r'_*`[' # Adding ` and [ just in case they appear in odd usernames
|
|
||||||
|
|
||||||
# Python's re.escape escapes all non-alphanumerics.
|
|
||||||
# We only want to escape specific markdown control characters within the username.
|
|
||||||
# For usernames, simply escaping '_' is often enough for the @mention issue.
|
|
||||||
return "".join(['\\' + char if char in escape_chars else char for char in text])
|
|
||||||
|
|
||||||
def format_last_participants_list(participants_list: list) -> str:
|
def format_last_participants_list(participants_list: list) -> str:
|
||||||
"""
|
"""
|
||||||
Formats the list of last participants for the announcement message.
|
Formats the list of last participants for the announcement message.
|
||||||
@@ -405,9 +389,9 @@ def format_last_participants_list(participants_list: list) -> str:
|
|||||||
if numbers_str:
|
if numbers_str:
|
||||||
num_list = numbers_str.split(',')
|
num_list = numbers_str.split(',')
|
||||||
if len(num_list) == 1:
|
if len(num_list) == 1:
|
||||||
line = f" - {escape_markdown_v2_chars_for_username(user_name)}, con la papeleta: {num_list[0]}"
|
line = f" - {user_name}, con la papeleta: {num_list[0]}"
|
||||||
else:
|
else:
|
||||||
line = f" - {escape_markdown_v2_chars_for_username(user_name)}, con las papeletas: {', '.join(num_list)}"
|
line = f" - {user_name}, con las papeletas: {', '.join(num_list)}"
|
||||||
formatted_lines.append(line)
|
formatted_lines.append(line)
|
||||||
|
|
||||||
return "\n".join(formatted_lines) # Add a trailing newline
|
return "\n".join(formatted_lines) # Add a trailing newline
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import os # Import os to get BOT_TOKEN
|
|||||||
# Import necessary functions from your project structure
|
# Import necessary functions from your project structure
|
||||||
# Adjust the path if paypal_processor.py is not in the root 'app' directory
|
# Adjust the path if paypal_processor.py is not in the root 'app' directory
|
||||||
# Assuming it can access the other modules directly as in the docker setup:
|
# Assuming it can access the other modules directly as in the docker setup:
|
||||||
from helpers import generate_table_image, format_last_participants_list, escape_markdown_v2_chars_for_username, get_paypal_access_token
|
from helpers import generate_table_image, format_last_participants_list, get_paypal_access_token
|
||||||
from database import (
|
from database import (
|
||||||
get_user_by_invoice_id, confirm_reserved_numbers,
|
get_user_by_invoice_id, confirm_reserved_numbers,
|
||||||
get_raffle_name, get_raffle,
|
get_raffle_name, get_raffle,
|
||||||
@@ -222,18 +222,18 @@ def receive_paypal_payment(invoice_id, payment_status, payment_amount_str):
|
|||||||
# If it's the last number, update the main message and delete the participate button
|
# If it's the last number, update the main message and delete the participate button
|
||||||
if remaining_numbers_amount == 0:
|
if remaining_numbers_amount == 0:
|
||||||
keyboard = None
|
keyboard = None
|
||||||
main_announcement = f"🎯🏆🎯 **Sorteo '{raffle_name}' terminado** 🎯🏆🎯\n\n"
|
main_announcement = f"🎯🏆🎯 <b>Sorteo '{raffle_name}' terminado</b> 🎯🏆🎯\n\n"
|
||||||
main_announcement += f"{raffle_details['description']}\n\n"
|
main_announcement += f"{raffle_details['description']}\n\n"
|
||||||
main_announcement += f"🌍 Envío internacional: {'Sí ✅' if raffle_details['international_shipping'] else 'No ❌'}\n"
|
main_announcement += f"🌍 Envío internacional: {'Sí ✅' if raffle_details['international_shipping'] else 'No ❌'}\n"
|
||||||
main_announcement += f"💵 **Donación mínima:** {raffle_details['price']}€\n"
|
main_announcement += f"💵 <b>Donación mínima:</b> {raffle_details['price']}€\n"
|
||||||
main_announcement += f"📜 Normas y condiciones: {TYC_DOCUMENT_URL}"
|
main_announcement += f"📜 <b>Normas y condiciones:</b> {TYC_DOCUMENT_URL}"
|
||||||
main_message_id = get_main_message_id(raffle_id)
|
main_message_id = get_main_message_id(raffle_id)
|
||||||
requests.post(f"{TELEGRAM_API_URL}/editMessageCaption", json={
|
requests.post(f"{TELEGRAM_API_URL}/editMessageCaption", json={
|
||||||
"chat_id": channel_id_to_announce,
|
"chat_id": channel_id_to_announce,
|
||||||
"message_id": main_message_id,
|
"message_id": main_message_id,
|
||||||
"caption": main_announcement,
|
"caption": main_announcement,
|
||||||
"reply_markup": keyboard,
|
"reply_markup": keyboard,
|
||||||
"parse_mode": "Markdown"
|
"parse_mode": "HTML"
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
url = f"https://t.me/{BOT_NAME}?start=join_{raffle_id}"
|
url = f"https://t.me/{BOT_NAME}?start=join_{raffle_id}"
|
||||||
@@ -247,22 +247,22 @@ def receive_paypal_payment(invoice_id, payment_status, payment_amount_str):
|
|||||||
main_announcement = f"🏆 Sorteo '{raffle_name}' en progreso 🏆\n\n"
|
main_announcement = f"🏆 Sorteo '{raffle_name}' en progreso 🏆\n\n"
|
||||||
main_announcement += f"{raffle_details['description']}\n\n"
|
main_announcement += f"{raffle_details['description']}\n\n"
|
||||||
main_announcement += f"🌍 Envío internacional: {'Sí ✅' if raffle_details['international_shipping'] else 'No ❌'}\n"
|
main_announcement += f"🌍 Envío internacional: {'Sí ✅' if raffle_details['international_shipping'] else 'No ❌'}\n"
|
||||||
main_announcement += f"💵 **Donación mínima:** {raffle_details['price']}€\n"
|
main_announcement += f"💵 <b>Donación mínima:</b> {raffle_details['price']}€\n"
|
||||||
main_announcement += f"🗒️ Quedan {remaining_numbers_amount} participaciones disponibles. ¡Date prisa! 🗒️\n\n"
|
main_announcement += f"🗒️ Quedan {remaining_numbers_amount} participaciones disponibles. ¡Date prisa! 🗒️\n\n"
|
||||||
main_announcement += f"📜 Normas y condiciones: {TYC_DOCUMENT_URL}"
|
main_announcement += f"📜 <b>Normas y condiciones:</b> {TYC_DOCUMENT_URL}"
|
||||||
main_message_id = get_main_message_id(raffle_id)
|
main_message_id = get_main_message_id(raffle_id)
|
||||||
requests.post(f"{TELEGRAM_API_URL}/editMessageCaption", json={
|
requests.post(f"{TELEGRAM_API_URL}/editMessageCaption", json={
|
||||||
"chat_id": channel_id_to_announce,
|
"chat_id": channel_id_to_announce,
|
||||||
"message_id": main_message_id,
|
"message_id": main_message_id,
|
||||||
"caption": main_announcement,
|
"caption": main_announcement,
|
||||||
"reply_markup": keyboard,
|
"reply_markup": keyboard,
|
||||||
"parse_mode": "Markdown"
|
"parse_mode": "HTML"
|
||||||
})
|
})
|
||||||
|
|
||||||
last_other_participants = get_last_n_other_participants(raffle_id, n=4)
|
last_other_participants = get_last_n_other_participants(raffle_id, n=4)
|
||||||
last_participants_text = format_last_participants_list(last_other_participants)
|
last_participants_text = format_last_participants_list(last_other_participants)
|
||||||
|
|
||||||
escaped_current_user_name = escape_markdown_v2_chars_for_username(current_user_name)
|
escaped_current_user_name = current_user_name
|
||||||
|
|
||||||
numbers_text = ""
|
numbers_text = ""
|
||||||
if len(numbers) > 1:
|
if len(numbers) > 1:
|
||||||
@@ -317,7 +317,7 @@ def receive_paypal_payment(invoice_id, payment_status, payment_amount_str):
|
|||||||
logger.warning(f"Error deleting old message {update_message_id}: {e_del}. Will send new.")
|
logger.warning(f"Error deleting old message {update_message_id}: {e_del}. Will send new.")
|
||||||
|
|
||||||
# Always send new photo after delete attempt, ensures updated image is shown
|
# Always send new photo after delete attempt, ensures updated image is shown
|
||||||
new_msg_info = send_telegram_photo(channel_id_to_announce, image_path, caption=caption, keyboard=keyboard, parse_mode='Markdown')
|
new_msg_info = send_telegram_photo(channel_id_to_announce, image_path, caption=caption, keyboard=keyboard, parse_mode='HTML')
|
||||||
if new_msg_info and isinstance(new_msg_info, dict) and 'message_id' in new_msg_info: # If send_telegram_photo returns message object
|
if new_msg_info and isinstance(new_msg_info, dict) and 'message_id' in new_msg_info: # If send_telegram_photo returns message object
|
||||||
sent_or_edited_message_id = new_msg_info['message_id']
|
sent_or_edited_message_id = new_msg_info['message_id']
|
||||||
elif isinstance(new_msg_info, bool) and new_msg_info is True: # If it just returns True/False
|
elif isinstance(new_msg_info, bool) and new_msg_info is True: # If it just returns True/False
|
||||||
@@ -328,7 +328,7 @@ def receive_paypal_payment(invoice_id, payment_status, payment_amount_str):
|
|||||||
|
|
||||||
else: # No previous message, send new
|
else: # No previous message, send new
|
||||||
logger.info(f"No previous message found for raffle {raffle_id} in channel {channel_id_to_announce}. Sending new.")
|
logger.info(f"No previous message found for raffle {raffle_id} in channel {channel_id_to_announce}. Sending new.")
|
||||||
new_msg_info = send_telegram_photo(channel_id_to_announce, image_path, caption=caption, keyboard=keyboard, parse_mode='Markdown')
|
new_msg_info = send_telegram_photo(channel_id_to_announce, image_path, caption=caption, keyboard=keyboard, parse_mode='HTML')
|
||||||
# Similar logic to get sent_or_edited_message_id as above
|
# Similar logic to get sent_or_edited_message_id as above
|
||||||
if new_msg_info and isinstance(new_msg_info, dict) and 'message_id' in new_msg_info:
|
if new_msg_info and isinstance(new_msg_info, dict) and 'message_id' in new_msg_info:
|
||||||
sent_or_edited_message_id = new_msg_info['message_id']
|
sent_or_edited_message_id = new_msg_info['message_id']
|
||||||
|
|||||||
Reference in New Issue
Block a user