diff --git a/README.md b/README.md index abf44a0..1773332 100644 --- a/README.md +++ b/README.md @@ -1,222 +1,162 @@ -# 📚 Anny Booking Automation +# Anny Booking Automation -This Python project automates booking of study spaces or resources via the [anny.eu](https://anny.eu) platform used by university library systems. It logs in automatically using SAML SSO (with pluggable provider support), searches for available slots, and makes bookings without user interaction — ideal for recurring reservations. +Automate study space reservations on [anny.eu](https://anny.eu) platforms used by university libraries. Logs in via SAML SSO, finds available slots, and books them automatically. ---- +## Features -## ⚙️ Features +- **Pluggable SSO** - KIT provider included, easily extendable for other universities +- **Smart scheduling** - Waits until midnight when slots open, or runs immediately for testing +- **Configurable time slots** - Set your preferred booking times in order of priority +- **Automated execution** - Runs via GitHub Actions + cron-job.org for precise timing -- 🔐 Pluggable SSO providers (KIT included, easily extendable for other universities) -- 📆 Configurable 3-days-ahead reservation window -- 🔎 Auto-detection of available time slots -- ⏳ Smart midnight wait: only waits if within 10 minutes of midnight, otherwise executes immediately -- 🛠️ Clean and modular object-oriented codebase -- 🔁 Fully automated execution using [cron-job.org](https://cron-job.org) + GitHub API -- 📦 Easy to extend and maintain +## Quick Start ---- - -## 🗂️ Project Structure - -``` -anny_booking/ -├── .env # Credentials (excluded from version control) -├── main.py # Entry point for script execution -├── requirements.txt # Python dependencies -│ -├── auth/ -│ ├── session.py # AnnySession class (login logic) -│ └── providers/ # SSO provider implementations -│ ├── __init__.py # Provider registry -│ ├── base.py # Abstract base class for providers -│ └── kit.py # KIT (Karlsruhe) SSO provider -│ -├── booking/ -│ └── client.py # BookingClient class (resource booking) -│ -├── config/ -│ └── constants.py # API URLs, timezone, SSO provider, and shared constants -│ -├── utils/ -│ └── helpers.py # Utility functions -│ -└── .github/ - └── workflows/ - └── schedule.yml # GitHub Actions workflow (manual trigger) -``` - ---- - -## 🚀 Setup & Installation - -### 1. Clone the repository +### 1. Clone and install ```bash -git clone https://github.com/your-username/anny-booking-automation.git +git clone https://github.com/wiestju/anny-booking-automation.git cd anny-booking-automation -``` - -### 2. Set up a Python virtual environment - -```bash -python3 -m venv venv -source venv/bin/activate +python -m venv venv +source venv/bin/activate # Windows: venv\Scripts\activate pip install -r requirements.txt ``` -### 3. Configure environment variables +### 2. Configure credentials -Create a `.env` file in the root of the project: +Create a `.env` file: -``` -USERNAME=your_kit_username -PASSWORD=your_kit_password +```env +USERNAME=your_university_username +PASSWORD=your_university_password ``` -> 🔒 Never commit this file to version control! +### 3. Configure booking settings -### 4. Configure SSO provider - -In `config/constants.py`, set your SSO provider: +Edit `config/constants.py`: ```python -SSO_PROVIDER = "kit" # Available: kit (add more in auth/providers/) +SSO_PROVIDER = "kit" # Your university's SSO provider +RESOURCE_ID = None # Auto-detect, or set a specific resource ID + +BOOKING_TIMES = [ + {'start': '14:00:00', 'end': '19:00:00'}, # First priority + {'start': '09:00:00', 'end': '13:00:00'}, # Second priority + {'start': '20:00:00', 'end': '23:45:00'}, # Third priority +] ``` ---- +### 4. Run locally -## 🔌 Adding a New SSO Provider - -To add support for another university (e.g., TUM), create a new file `auth/providers/tum.py`: - -```python -from auth.providers.base import SSOProvider -from utils.helpers import extract_html_value - -class TUMProvider(SSOProvider): - name = "TUM" - domain = "tum.de" - - def authenticate(self) -> str: - # Implement TUM-specific SAML authentication - # Use self.session, self.redirect_response, self.username, self.password - # Return the HTML containing the SAMLResponse - pass +```bash +python main.py ``` -Then register it in `auth/providers/__init__.py`: +## Automated Scheduling -```python -from auth.providers.tum import TUMProvider +For daily automated bookings, use GitHub Actions triggered by [cron-job.org](https://cron-job.org). -PROVIDERS: dict[str, type[SSOProvider]] = { - "kit": KITProvider, - "tum": TUMProvider, -} +### Why cron-job.org instead of GitHub's schedule? + +GitHub Actions `on: schedule` has two issues: +- **Queue delays** - Workflows can be delayed by minutes, missing the booking window +- **UTC only** - No timezone support, requiring manual DST adjustments + +cron-job.org provides precise, timezone-aware scheduling with no delays. + +### Setup + +#### 1. Add GitHub Secrets + +In your repository: **Settings > Secrets and variables > Actions** + +| Secret | Value | +|--------|-------| +| `USERNAME` | Your university username | +| `PASSWORD` | Your university password | + +#### 2. Create a GitHub Personal Access Token + +1. Go to [GitHub Settings > Developer settings > Personal access tokens](https://github.com/settings/tokens) +2. Generate a token with `repo` scope (or fine-grained with Actions read/write) +3. Copy the token + +#### 3. Configure cron-job.org + +Create a new cron job with these settings: + +| Setting | Value | +|---------|-------| +| URL | `https://api.github.com/repos/YOUR_USERNAME/anny-booking-automation/actions/workflows/schedule.yml/dispatches` | +| Schedule | `58 23 * * *` (23:58 daily) | +| Timezone | `Europe/Berlin` (or your timezone) | +| Request method | `POST` | + +**Headers:** +``` +Authorization: Bearer YOUR_GITHUB_TOKEN +Content-Type: application/json +Accept: application/vnd.github.v3+json ``` ---- - -## ⏱️ Automated Execution via cron-job.org + GitHub API - -To maximize booking success, the script is triggered **two minutes before midnight** (e.g., 23:58). -It logs in to the KIT SAML SSO in advance, keeps the session alive, and **waits internally until exactly 00:00** to instantly book the best available slot as soon as new reservations open. - -The trigger is handled by: - -- `cron-job.org` for precise scheduling (e.g., 23:58 Europe/Berlin) -- GitHub Actions to run the actual script with credentials passed via GitHub Secrets - -### Why not just use `on: schedule`? - -GitHub Actions only supports fixed cron expressions (e.g., once per hour) and does **not** allow more frequent triggers like every 5 or 10 minutes. -Additionally, it suffers from two major issues: - -- ⏳ **Queue delay**: Workflows triggered via GitHub's `schedule` event are sometimes delayed by several minutes due to internal queue congestion. This can cause the booking script to miss the optimal reservation window. -- 🕒 **Timezone limitations**: GitHub's cron system uses UTC without native timezone support. That means you need to manually convert your desired local time (e.g. Europe/Berlin) and keep adjusting for daylight saving time changes. - -For these reasons, we use [cron-job.org](https://cron-job.org), which offers: - -- ✅ Precise minute-level scheduling -- ✅ Native timezone selection (e.g. Europe/Berlin) -- ✅ Immediate webhook execution with no delay - -This ensures your booking script runs exactly when needed — already logged in and ready to act at 00:00. - ---- - -### How it works - -1. `cron-job.org` triggers the GitHub Actions workflow at **23:58** (Europe/Berlin). -2. The script logs in via your configured SSO provider and maintains an active session. -3. If within 10 minutes of midnight, it waits until **00:00**. Otherwise, it executes immediately (useful for testing or manual runs). -4. As soon as new booking slots are released, it instantly reserves the first suitable slot. - ---- - -### Setup steps - -1. Set up a cron job on `cron-job.org` that sends a POST request to: - -``` -https://api.github.com/repos/your-username/anny-booking-automation/actions/workflows/schedule.yml/dispatches -``` - -2. Include the following JSON payload: - +**Request body:** ```json {"ref": "main"} ``` -3. Add this header: +### How it works + +1. cron-job.org triggers the workflow at **23:58** +2. The script logs in and establishes a session +3. It waits until **00:00** when new slots become available +4. Instantly books the first available slot from your priority list + +## Project Structure ``` -Authorization: Bearer YOUR_GITHUB_PERSONAL_ACCESS_TOKEN -Content-Type: application/json -Accept: application/vnd.github.v3+json +anny-booking-automation/ +├── main.py # Entry point +├── config/ +│ └── constants.py # URLs, timezone, booking times +├── auth/ +│ ├── session.py # Login session handling +│ └── providers/ # SSO provider implementations +│ ├── base.py # Abstract base class +│ └── kit.py # KIT provider +├── booking/ +│ └── client.py # Booking API client +└── utils/ + └── helpers.py # Utility functions ``` -4. Your GitHub workflow (`.github/workflows/schedule.yml`) listens for this event: +## Adding a New SSO Provider -```yaml -name: Daily Library Reservation Automation +Create `auth/providers/youruni.py`: -on: - workflow_dispatch: +```python +from auth.providers.base import SSOProvider -jobs: - run-script: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dependencies - run: | - pip install -r requirements.txt - - name: Run script - env: - USERNAME: ${{ secrets.USERNAME }} - PASSWORD: ${{ secrets.PASSWORD }} - run: | - python main.py +class YourUniProvider(SSOProvider): + name = "youruni" + domain = "youruni.edu" + + def authenticate(self) -> str: + # Implement SAML authentication flow + # Use self.session, self.redirect_response, self.username, self.password + # Return HTML containing SAMLResponse + pass ``` -> 💡 Store your university credentials securely as [GitHub Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets): `USERNAME` and `PASSWORD`. +Register in `auth/providers/__init__.py`: ---- +```python +from auth.providers.youruni import YourUniProvider -## 🤝 Contributing +PROVIDERS = { + "kit": KITProvider, + "youruni": YourUniProvider, +} +``` -Pull requests are welcome! Feel free to open an issue or suggest features or improvements. +## License ---- - -## 📎 Related Tools - -- [cron-job.org](https://cron-job.org) -- [GitHub Actions](https://github.com/features/actions) -- [anny.eu](https://anny.eu) +MIT