SecretarX/xclient.py
2024-09-10 14:11:50 +02:00

295 lines
11 KiB
Python

from selenium import webdriver
from models import Booking
import json
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import requests
import config
from selenium.webdriver.chrome.service import Service
class XClient:
def __init__(self, username, password):
self.username = username
self.password = password
self.access_token = None
self.id = -1
self.session = requests.Session()
# Default headers for session that mimicks browser
self.session.headers.update(
{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
}
)
def fetch_access_token(self):
# Set up the WebDriver (make sure to use the correct path for your WebDriver)
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
service = Service(executable_path=config.CHROMEDRIVER_PATH)
driver = webdriver.Chrome(options=options, service=service)
driver.get("https://x.tudelft.nl")
button = WebDriverWait(driver, 30).until(
EC.element_to_be_clickable(
(By.XPATH, "//span[contains(text(), 'TUDelft')]")
)
)
button.click()
button = WebDriverWait(driver, 30).until(
EC.element_to_be_clickable(
(
By.XPATH,
"//h3[contains(text(), 'Delft University of Technology')]",
)
)
)
button.click()
# Input the username
username_input = driver.find_element(By.ID, "username")
username_input.send_keys(self.username) # Replace with your actual username
password_input = driver.find_element(By.ID, "password")
password_input.send_keys(self.password)
password_input.submit()
time.sleep(1)
delcom_auth = json.loads(
driver.execute_script("return window.localStorage.getItem('delcom_auth');")
)
driver.quit()
access_token = delcom_auth["tokenResponse"].get("accessToken", None)
return access_token
def list_slots(self, start, end, tagIDs=[28]):
"""
URl:
https://backbone-web-api.production.delft.delcom.nl/bookable-slots
Get parameters:
s: {"startDate":"2024-09-09T14:23:24.934Z","endDate":"2024-09-09T22:00:00.000Z","tagIds":{"$in":[28]},"availableFromDate":{"$gt":"2024-09-09T14:23:24.935Z"},"availableTillDate":{"$gte":"2024-09-09T14:23:24.934Z"}}
join: product
"""
self.login_if_necessary()
start_stamp = start.strftime("%Y-%m-%dT%H:%M:%S.000Z")
end_stamp = end.strftime("%Y-%m-%dT%H:%M:%S.000Z")
response = self.session.get(
"https://backbone-web-api.production.delft.delcom.nl/bookable-slots",
params={
"s": json.dumps(
{
"startDate": start_stamp,
"endDate": end_stamp,
"tagIds": {"$in": tagIDs},
"availableFromDate": {"$gt": start_stamp},
"availableTillDate": {"$gte": start_stamp},
}
),
"join": ["product", "linkedProduct"],
},
headers={"Authorization": f"Bearer {self.access_token}"},
)
return [Booking(**x) for x in response.json()["data"]]
def my_bookings(self, start, end):
# Ensure user is logged in
self.login_if_necessary()
# Replace this with the actual member ID of the logged-in user
member_id = self.user_id # Assuming it's stored when the user logs in
# Define the URL
url = "https://backbone-web-api.production.delft.delcom.nl/participations"
# Create the parameters for the request
params = {
"s": json.dumps(
{
"$or": [
{"memberId": member_id}, # Fetch bookings based on the user ID
],
"booking.startDate": {
"$gte": start.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
},
"booking.endDate": {"$lte": end.strftime("%Y-%m-%dT%H:%M:%S.%fZ")},
},
),
"join": ["booking", "linkedProduct", "product"],
"fetchOptimizedCustomerDashboard": "true",
"fetchTicket": "false",
"sort": "booking.startDate,ASC",
}
# Make the GET request to the API
response = self.session.get(
url,
params=params,
headers={"Authorization": f"Bearer {self.access_token}"},
)
# Check if the request was successful
if response.status_code == 200:
bookings = [
Booking(**(x | {"bookingId": x["id"], "bookableProductId": None}))
for x in response.json()["data"]["bookings"]
]
return bookings
elif response.status_code == 403:
raise Exception(f"Failed to fetch bookings: {response.json()['message']}")
else:
raise Exception(
f"Failed to fetch bookings: {response.status_code}, {response.text}"
)
def make_booking(self, booking):
# Ensure that we're logged in
self.login_if_necessary()
# Extract necessary details from the booking object
booking_data = {
"organizationId": None, # This is None in the example curl
"memberId": self.user_id, # This is fetched during login
"bookingId": booking.booking_id, # Booking ID from the booking object
"primaryPurchaseMessage": None,
"secondaryPurchaseMessage": None,
"params": {
"startDate": booking.start.strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
"endDate": booking.end.strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
"bookableProductId": booking.bookable_product_id,
"bookableLinkedProductId": booking.linked_product_id,
"bookingId": booking.booking_id, # Booking ID again
"invitedMemberEmails": [],
"invitedGuests": [],
"invitedOthers": [],
"primaryPurchaseMessage": None,
"secondaryPurchaseMessage": None,
},
"dateOfRegistration": None,
}
# Send the booking request
response = self.session.post(
"https://backbone-web-api.production.delft.delcom.nl/participations",
json=booking_data,
headers={"Authorization": f"Bearer {self.access_token}"},
)
# Check the response status
if response.status_code == 201:
return response.json() # Return response if successful
elif response.status_code == 403:
raise Exception(f"Failed to make booking: {response.json()['message']}")
else:
raise Exception(
f"Failed to make booking: {response.status_code}, {response.text}"
)
def check_booking_availability(self, booking):
# Ensure user is logged in
self.login_if_necessary()
# Define the URL
url = "https://backbone-web-api.production.delft.delcom.nl/bookable-slots"
# Create the parameters for the request
params = {
"s": json.dumps(
{
"bookingId": booking.booking_id,
"tagIds": {"$in": [28]},
},
),
"join": ["product", "linkedProduct"],
}
# Make the GET request to the API
response = self.session.get(
url,
params=params,
headers={"Authorization": f"Bearer {self.access_token}"},
)
# Check if the request was successful
if response.status_code == 200:
if len(response.json()["data"]) != 1:
return False
booking = Booking(**response.json()["data"][0])
return booking.available
elif response.status_code == 403:
raise Exception(
f"Failed to check booking availability: {response.json()['message']}"
)
else:
raise Exception(
f"Failed to check booking availability: {response.status_code}, {response.text}"
)
@property
def user_id(self):
if self.id != -1:
return self.id
self.login_if_necessary()
response = self.session.get(
"https://backbone-web-api.production.delft.delcom.nl/auth?cf=0",
headers={"Authorization": f"Bearer {self.access_token}"},
)
self.id = response.json()["id"]
return self.id
def cancel_booking(self, booking):
participation_id = None
for i in booking.booking_dict.get("participations", []):
if i["memberId"] == self.user_id:
participation_id = i["id"]
break
assert (
participation_id is not None
), "Booking not found in user's participations"
## Send a DELETE request to the URL https://backbone-web-api.production.delft.delcom.nl/participations/{id}
response = self.session.delete(
f"https://backbone-web-api.production.delft.delcom.nl/participations/{participation_id}",
headers={"Authorization": f"Bearer {self.access_token}"},
)
if response.status_code == 200:
return True
elif response.status_code == 403:
raise Exception(f"Failed to cancel booking: {response.json()['message']}")
else:
raise Exception(
f"Failed to fetch bookings: {response.status_code}, {response.text}"
)
def login(self):
self.access_token = self.fetch_access_token()
def is_logged_in(self):
# Send request to 'https://backbone-web-api.production.delft.delcom.nl/auth?cf=0 with access token as bearer, check status code
response = self.session.get(
"https://backbone-web-api.production.delft.delcom.nl/auth?cf=0",
headers={"Authorization": f"Bearer {self.access_token}"},
)
return response.status_code == 200
def login_if_necessary(self):
if not self.is_logged_in():
self.login()