import requests from requests.exceptions import JSONDecodeError from config.constants import RESOURCE_URL, BOOKING_API_BASE, CHECKOUT_FORM_API, ANNY_BASE_URL, SERVICE_ID class BookingClient: def __init__(self, cookies): self.session = requests.Session() self.session.cookies = cookies self.token = cookies.get('anny_shop_jwt') self.session.headers.update({ 'authorization': f'Bearer {self.token}', 'content-type': 'application/vnd.api+json', 'origin': ANNY_BASE_URL, 'referer': ANNY_BASE_URL + '/', 'user-agent': 'Mozilla/5.0' }) def find_available_resource(self, start, end): response = self.session.get(RESOURCE_URL, params={ 'page[number]': 1, 'page[size]': 250, 'filter[available_from]': start, 'filter[available_to]': end, 'filter[availability_exact_match]': 1, 'filter[exclude_hidden]': 0, 'filter[exclude_child_resources]': 0, 'filter[availability_service_id]': int(SERVICE_ID), 'filter[include_unavailable]': 0, 'filter[pre_order_ids]': '', 'sort': 'name' }) if not response.ok: print(f"❌ Failed to fetch resources: HTTP {response.status_code}") return None try: resources = response.json().get('data', []) except (ValueError, JSONDecodeError): print(f"❌ Invalid JSON response when fetching resources: {response.text}") # print(f"❌ Invalid JSON response when fetching resources: {response.text[:200]}") return None return resources[-1]['id'] if resources else None def reserve(self, resource_id, start, end): print(start) print(end) booking = self.session.post( f"{BOOKING_API_BASE}/order/bookings", params={ 'stateless': '1', 'include': 'customer,voucher,bookings.booking_add_ons.add_on.cover_image,bookings.sub_bookings.resource,bookings.sub_bookings.service,bookings.customer,bookings.service.custom_forms.custom_fields,bookings.service.add_ons.cover_image,bookings.service.add_ons.group,bookings.cancellation_policy,bookings.resource.cover_image,bookings.resource.parent,bookings.resource.location,bookings.resource.category,bookings.reminders,bookings.booking_series,bookings.sequenced_bookings.resource,bookings.sequenced_bookings.service,bookings.sequenced_bookings.service.add_ons.cover_image,bookings.sequenced_bookings.service.add_ons.group,bookings.booking_participants,sub_orders.bookings,sub_orders.organization.legal_documents' }, json={ "resource_id": [resource_id], "service_id": {SERVICE_ID: 1}, "start_date": start, "end_date": end, "description": "", "customer_note": "", "add_ons_by_service": {SERVICE_ID: [[]]}, "sub_bookings_by_service": {}, # "booking_quota_grant_id":"24735199", "booking_quota_grant_id":"24735202", "strategy": "multi-resource" } # data = '{"resource_id":["15994"],"service_id":{"601":1},"start_date":"2026-02-03T22:30:00+01:00","end_date":"2026-02-03T23:30:00+01:00","description":"","customer_note":"","add_ons_by_service":{"601":[[]]},"sub_bookings_by_service":{},"booking_quota_grant_id":"24735199","strategy":"multi-resource"}' ) print("resource id: %s" % resource_id) if not booking.ok: print(f"❌ Booking failed: HTTP {booking.status_code}") return False try: data = booking.json().get("data", {}) except (ValueError, JSONDecodeError): # print(f"❌ Invalid JSON response from booking request: {booking.text[:200]}") print(f"❌ Invalid JSON response from booking request: {booking.text}") return False oid = data.get("id") oat = data.get("attributes", {}).get("access_token") if not oid or not oat: print("❌ Missing booking ID or access token in response") return False checkout = self.session.get(f"{CHECKOUT_FORM_API}?oid={oid}&oat={oat}&stateless=1") if not checkout.ok: print(f"❌ Checkout form failed: HTTP {checkout.status_code}") return False try: customer = checkout.json().get("default", {}).get("customer", {}) except (ValueError, JSONDecodeError): print(f"❌ Invalid JSON response from checkout form: {checkout.text}") # print(f"❌ Invalid JSON response from checkout form: {checkout.text[:200]}") return False final = self.session.post( f"{BOOKING_API_BASE}/order?oid={oid}&oat={oat}&stateless=1", json={ "customer": { "given_name": customer.get("given_name"), "family_name": customer.get("family_name"), "email": customer.get("email") }, "accept_terms": True, "payment_method": "", "success_url": f"{ANNY_BASE_URL}/checkout/success?oids={oid}&oats={oat}", "cancel_url": f"{ANNY_BASE_URL}/checkout?step=checkout&childResource={resource_id}", "meta": {"timezone": "Europe/Berlin"} } ) if final.ok: print("✅ Reservation successful!") return True print("❌ Reservation failed.") return False