From d71bfff10e281d2f355701a196f0ab2f95a156f5 Mon Sep 17 00:00:00 2001 From: kbe Date: Mon, 21 Jul 2025 20:29:44 +0200 Subject: [PATCH] feat: Make notifier and program async Using asyncio library, notification are now trigerred asynchronously. --- book_crossfit.py | 3 +- crossfit_booker.py | 16 ++++++---- docs/waiting_list.json | 72 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 docs/waiting_list.json diff --git a/book_crossfit.py b/book_crossfit.py index 604dc9c..9a3c727 100755 --- a/book_crossfit.py +++ b/book_crossfit.py @@ -2,6 +2,7 @@ import logging import traceback +import asyncio from crossfit_booker import CrossFitBooker if __name__ == "__main__": @@ -30,5 +31,5 @@ if __name__ == "__main__": exit(1) # Start the continuous booking loop - booker.run() + asyncio.run(booker.run()) logging.info("Script completed") diff --git a/crossfit_booker.py b/crossfit_booker.py index 9e1bb70..4bd3246 100644 --- a/crossfit_booker.py +++ b/crossfit_booker.py @@ -363,7 +363,9 @@ class CrossFitBooker: # First check if can_join is true (primary condition) if user_info.get("can_join", False): activity_name = session.get("name_activity") - logging.debug(f"Session is bookable: {activity_name} can_join is True") + activity_date = session.get("start_timestamp", "Unknown date") + activity_time = activity_date.split(" ")[1] if " " in activity_date else "Unknown time" + logging.debug(f"Session is bookable: {activity_name} on {activity_date} at {activity_time} - can_join is True") return True # If can_join is False, check if there's a booking window @@ -435,7 +437,7 @@ class CrossFitBooker: logging.error(f"Failed to check session: {str(e)} - Session: {session}") return False - def run_booking_cycle(self, current_time: datetime) -> None: + async def run_booking_cycle(self, current_time: datetime) -> None: """ Run one cycle of checking and booking sessions. @@ -487,7 +489,7 @@ class CrossFitBooker: if not session_time.tzinfo: session_time = pytz.timezone(TIMEZONE).localize(session_time) session_details = f"{session['name_activity']} at {session_time.strftime('%Y-%m-%d %H:%M')}" - self.notifier.notify_session_booking(session_details) + await self.notifier.notify_session_booking(session_details) logging.info(f"Notified about found preferred session: {session_details}") # Notify about upcoming sessions @@ -496,7 +498,7 @@ class CrossFitBooker: if not session_time.tzinfo: session_time = pytz.timezone(TIMEZONE).localize(session_time) session_details = f"{session['name_activity']} at {session_time.strftime('%Y-%m-%d %H:%M')}" - self.notifier.notify_upcoming_session(session_details, 1) # Days until is 1 for tomorrow + await self.notifier.notify_upcoming_session(session_details, 1) # Days until is 1 for tomorrow logging.info(f"Notified about upcoming session: {session_details}") # Book sessions (preferred first) @@ -507,12 +509,12 @@ class CrossFitBooker: if self.book_session(session["id_activity_calendar"]): # Send notification after successful booking session_details = f"{session['name_activity']} at {session_time.strftime('%Y-%m-%d %H:%M')}" - self.notifier.notify_session_booking(session_details) + await self.notifier.notify_session_booking(session_details) logging.info(f"Successfully booked {session_type} session at {session_time}") else: logging.error(f"Failed to book {session_type} session at {session_time} - Session: {session}") - def run(self) -> None: + async def run(self) -> None: """ Main execution loop. """ @@ -531,7 +533,7 @@ class CrossFitBooker: logging.info(f"Current time: {current_time}") # Always run booking cycle to check for preferred sessions and notify - self.run_booking_cycle(current_time) + await self.run_booking_cycle(current_time) # Run booking cycle at the target time for actual booking target_time_str = current_time.strftime("%H:%M") diff --git a/docs/waiting_list.json b/docs/waiting_list.json new file mode 100644 index 0000000..6c66f99 --- /dev/null +++ b/docs/waiting_list.json @@ -0,0 +1,72 @@ +curl 'https://sport.nubapp.com/api/v4/activities/activity-calendar/19291431/basicInfo?app_version=5.09.21^&id_application=81560887' \ + -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0' \ + -H 'Accept: application/json, text/plain, */*' \ + -H 'Accept-Language: en-GB,en;q=0.8,fr-FR;q=0.5,fr;q=0.3' \ + -H 'Accept-Encoding: gzip, deflate, br, zstd' \ + -H 'Referer: https://box.resawod.com/' \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -H 'Nubapp-Origin: user_apps' \ + -H 'Origin: https://box.resawod.com' \ + -H 'Sec-GPC: 1' \ + -H 'Connection: keep-alive' \ + -H 'Sec-Fetch-Dest: empty' \ + -H 'Sec-Fetch-Mode: cors' \ + -H 'Sec-Fetch-Site: cross-site' \ + -H 'Priority: u=0' \ + -H 'TE: trailers' + +{ + "message": "All right! Here is your data.", + "data": { + "activity_calendar": { + "id_activity_calendar": 19291431, + "name_activity": "CONDITIONING LOUVRE 3", + "description": "\\u003Cp\\u003EWod Condi, de la simplicit\\u00e9 mais beaucoup d\\u0027intensit\\u00e9... des erg mais pas seulement, des mouvements de CrossFit qui vous permettrons d\\u0027am\\u00e9liorer vos capacit\\u00e9s a\\u00e9robies\\u003C\\/p\\u003E", + "id_activity": 43932, + "id_category_activity": 677, + "start_timestamp": "2025-07-23 18:30:00", + "end_timestamp": "2025-07-23 19:15:00", + "n_inscribed": 12, + "n_capacity": 12, + "n_waiting_list": 3, + "n_capacity_waiting_list": 8, + "id_trainer": null, + "is_course": false, + "cancelled": false, + "guest_info": { + "can_join": true, + "available_booking_options": [], + "errors": 0, + "errors_by_memberships": [], + "join_as_normal_user": false, + "vouchers": [], + "voucher_autopay": false, + "unableToBookUntilDate": null, + "unableToBookUntilTime": null, + "best_booking_option": { + "type": 2, + "price": 0, + "membershipId": null, + "discountId": null, + "discountValue": null + }, + "price": 0, + "price_befdis": 0, + "user_attending": false, + "user_waiting": false + }, + "facilities": [], + "image": null, + "color": "#a74cd6", + "max_guests": 0, + "guest_prices": { + "1": 0, + "2": 0, + "3": 0 + }, + "id_language": null + } + }, + "success": true, + "status": 200 +} \ No newline at end of file