Skip to content

Claude/greek visa bot v t43 m#3

Open
recepkocofice-hue wants to merge 3 commits intoismkdc:mainfrom
recepkocofice-hue:claude/greek-visa-bot-vT43M
Open

Claude/greek visa bot v t43 m#3
recepkocofice-hue wants to merge 3 commits intoismkdc:mainfrom
recepkocofice-hue:claude/greek-visa-bot-vT43M

Conversation

@recepkocofice-hue
Copy link

@recepkocofice-hue recepkocofice-hue commented Jan 2, 2026

Summary by CodeRabbit

  • New Features

    • End-to-end visa bot: slot monitoring, Telegram alerts, optional auto-booking with CAPTCHA fallback, and state persistence.
    • Docker-ready deployment with compose support and persistent volumes.
  • Configuration

    • Added an example environment template and environment-driven configuration with validation.
  • Documentation

    • Expanded README with quick start, architecture, CLI, and troubleshooting.
  • Chores

    • Updated dependencies and ignore rules; added container image setup.

✏️ Tip: You can customize this high-level summary in your review settings.

claude added 2 commits January 2, 2026 19:55
- reservation_bot.py: API-based booking bot with slot monitoring
- browser_bot.py: Playwright browser automation for form filling
- config.py: Environment-based configuration management
- requirements.txt: Project dependencies (curl_cffi, playwright)
- .env.example: Configuration template
- Updated readme.md with comprehensive documentation

Educational/theoretical implementation demonstrating:
- Web API interaction and scraping
- Browser automation with anti-detection
- Telegram notification integration
- hybrid_bot.py: Production-ready bot combining fast API monitoring
  with reliable browser-based booking
  - Parallel date scanning (~2s for 30 days)
  - SQLite state persistence (survives restarts)
  - Smart slot filtering (dates, times, quota)
  - CAPTCHA detection with manual solving flow
  - CLI interface with multiple options
  - Graceful shutdown handling

- Docker support:
  - Dockerfile with Playwright/Chromium
  - docker-compose.yml for easy deployment

- Updated .env.example with all configuration options
- Comprehensive README with architecture diagrams
@coderabbitai
Copy link

coderabbitai bot commented Jan 2, 2026

📝 Walkthrough

Walkthrough

Adds a Kosmos Visa Appointment Bot suite: environment/config templates, three bot implementations (browser, API, hybrid), Docker/Docker Compose setup, dependencies, rewritten README, and SQLite state persistence. Provides slot monitoring, Telegram notifications, optional Playwright auto-booking, and CLI orchestration.

Changes

Cohort / File(s) Summary
Configuration & Environment
​.env.example, config.py
Adds an example .env template and a typed, environment-driven config.py with enums, dataclasses (ApplicantConfig, TelegramConfig, BotConfig), parsing helpers, load/validate functions, and basic validation
Browser Automation Bot
browser_bot.py
New Playwright-based bot implementing KosmosBrowserBot: async browser lifecycle, API slot checks, UI navigation, appointment selection, form autofill, CAPTCHA handling (manual/headless), booking submission, screenshots, and Telegram notifications
Hybrid Bot (Main Orchestrator)
hybrid_bot.py
New orchestration module combining fast API monitoring, SlotMonitor, StateManager (SQLite), TelegramNotifier, BrowserBooker, and HybridBot CLI; handles scanning, notifications, optional auto-booking, graceful shutdown, and state persistence
API/Reservation Bot (Alternative)
reservation_bot.py
Adds an alternative reservation-style bot (theoretical) with session-based booking flow, slot scanning/filtering, booking payload handling, retry logic, and Telegram notifications
Containerization & Compose
Dockerfile, docker-compose.yml
Adds Dockerfile (python:3.11-slim, Playwright setup, installs deps, creates data/screenshot dirs) and docker-compose with volumes, env_file, resource limits, healthcheck, and logging config
Documentation & Dependencies
readme.md, requirements.txt
Full README rewrite (features, architecture, quick start, CLI, troubleshooting) and requirements adding curl_cffi, python-dotenv, and playwright
Project Hygiene
.gitignore
Expanded .gitignore to exclude .env, DB files, screenshots, logs, caches, and common IDE artifacts

Sequence Diagram(s)

sequenceDiagram
    participant Bot as HybridBot
    participant Monitor as SlotMonitor
    participant API as Visa API
    participant Notifier as TelegramNotifier
    participant Booker as BrowserBooker
    participant Portal as Booking Portal
    participant DB as SQLite State

    rect rgb(230,245,255)
    Note over Bot,DB: Initialization
    Bot->>DB: Load state (notified slots, successful bookings)
    Bot->>Notifier: notify_bot_started()
    end

    rect rgb(240,255,240)
    Note over Bot,API: Monitoring Loop (periodic)
    loop Every check_interval
        Bot->>Monitor: scan_all_dates()
        Monitor->>API: check_date() for each day
        API-->>Monitor: return available slots
        Monitor->>Monitor: filter_by_time (preferred_times)
        Monitor-->>Bot: list[Slot]
    end
    end

    rect rgb(255,250,240)
    Note over Bot,Notifier: Notification Flow
    Bot->>DB: is_slot_notified(slot_key)?
    alt Slot not yet notified
        Bot->>DB: mark_slot_notified(slot_key)
        Bot->>Notifier: notify_slot_available(slot)
        Notifier-->>Notifier: format HTML message
        Notifier-->>Telegram: send alert
    end
    end

    rect rgb(255,245,245)
    Note over Bot,Portal: Auto-Booking Flow (if enabled & not yet booked)
    alt auto_book enabled AND no successful booking yet
        Bot->>Booker: book_slot(slot)
        Booker->>Booker: setup() - launch browser
        Booker->>Portal: navigate_to_booking()
        Booker->>Portal: select_appointment_type()
        Booker->>Portal: select_date_and_time()
        Booker->>Portal: fill_applicant_form()
        Booker->>Portal: wait for CAPTCHA
        alt CAPTCHA detected
            Booker->>Notifier: notify_captcha_required()
            Notifier-->>Telegram: send CAPTCHA alert (manual intervention)
            Booker->>Booker: wait for manual completion
        end
        Booker->>Portal: submit_booking()
        Portal-->>Booker: confirmation page
        Booker->>Booker: extract_confirmation()
        Booker->>Booker: take_screenshot()
        Booker->>DB: log_successful_booking()
        Booker->>Notifier: notify_booking_success(confirmation_id)
        Notifier-->>Telegram: send success notification
        Booker->>Booker: close() - cleanup browser
    else Booking fails
        Booker->>DB: log_booking_attempt(status=failed)
        Booker->>Notifier: notify_booking_failed(error)
        Notifier-->>Telegram: send failure alert
    end
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🐰 A visa bot hops with glee,
With Telegram alerts set wild and free,
From API scans to Playwright's dance,
It books your slots—give chance a chance!
Docker containers, state persisted true,
Pure automation dreams, now in view! 🎫✨

Pre-merge checks

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.54% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Claude/greek visa bot v t43 m' is vague and uses branch naming conventions rather than describing the actual changes; it lacks clarity about what was implemented. Use a descriptive title summarizing the main change, such as 'Add hybrid visa appointment bot with API monitoring and browser booking' or 'Implement Kosmos visa bot with slot monitoring and auto-booking features'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36f3077 and 142bd37.

📒 Files selected for processing (1)
  • .gitignore
✅ Files skipped from review due to trivial changes (1)
  • .gitignore

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Nitpick comments (15)
readme.md (3)

20-20: Add language specifiers to fenced code blocks.

Markdown best practice: all code blocks should declare their language for syntax highlighting and linting clarity.

🔎 Proposed fixes
-```
+```
 kosmos-vize-bot/
 ├── hybrid_bot.py       # ⭐ RECOMMENDED - Hybrid approach
 ...
-```
+```

And similarly for line 92 (the ASCII architecture diagram). For ASCII art without code, you can use:

-```
+```text
 ┌─────────────────────────────────────────────────────────────────┐
 │                      HYBRID BOT FLOW                            │
 ...
-```
+```text

Also applies to: 92-92


85-85: Remove tautological phrase "CLI interface".

The term "CLI" already means "Command-Line Interface"; saying "CLI interface" is redundant. Simplify to "CLI" in the comparison table.

🔎 Proposed fix
-| CLI interface | ✅ | ❌ | ❌ |
+| CLI | ✅ | ❌ | ❌ |

222-232: Use heading structure instead of emphasis for section boundary.

Lines 221–222 use bold emphasis (**EDUCATIONAL PURPOSE ONLY**) as a de-facto heading, but Markdown prefers explicit heading syntax for semantic clarity and proper document structure.

🔎 Proposed fix
-## Disclaimer
-
-**EDUCATIONAL PURPOSE ONLY**
-
-This project demonstrates:
+## Disclaimer
+
+### Educational Purpose Only
+
+This project demonstrates:
browser_bot.py (2)

572-581: Hardcoded PII in example code.

The main() function contains hardcoded example personal data. While labeled as example, this could be accidentally committed with real data or copied without modification.

🔎 Proposed fix - load from environment variables
 async def main():
     """Example usage"""

     # Applicant information
-    applicant = ApplicantInfo(
-        first_name="John",
-        last_name="Doe",
-        nationality_number="12345678901",
-        passport_number="U12345678",
-        birth_date="1990-01-15",
-        email="john.doe@example.com",
-        phone="+905551234567"
-    )
+    applicant = ApplicantInfo(
+        first_name=os.getenv('APPLICANT_FIRST_NAME', ''),
+        last_name=os.getenv('APPLICANT_LAST_NAME', ''),
+        nationality_number=os.getenv('APPLICANT_NATIONALITY_NUMBER', ''),
+        passport_number=os.getenv('APPLICANT_PASSPORT_NUMBER', ''),
+        birth_date=os.getenv('APPLICANT_BIRTH_DATE', ''),
+        email=os.getenv('APPLICANT_EMAIL', ''),
+        phone=os.getenv('APPLICANT_PHONE', '')
+    )

180-189: Synchronous blocking call in async context.

requests.get from curl_cffi is a synchronous call inside an async method. This blocks the event loop and degrades concurrency.

🔎 Proposed fix - run in executor like hybrid_bot.py does
     async def check_availability_api(self, date: str) -> List[dict]:
         """
         Check availability using the API (faster than browser)
         Uses curl_cffi for browser impersonation
         """
         url = f"{self.API_URL}/AppointmentLayouts/GetAppointmentHourQoutaInfo"
         # ... params and headers setup ...

         try:
-            response = requests.get(url, headers=headers, params=params, impersonate="chrome")
+            loop = asyncio.get_event_loop()
+            response = await loop.run_in_executor(
+                None,
+                lambda: requests.get(url, headers=headers, params=params, impersonate="chrome", timeout=10)
+            )
             if response.status_code == 200:
config.py (2)

42-45: Incomplete DealerId enum compared to reservation_bot.py.

reservation_bot.py defines ISTANBUL, ANKARA, and IZMIR, but this file only has ISTANBUL. Consider consolidating enum definitions in one place.

🔎 Proposed fix
 class DealerId(Enum):
     """Known dealer/location IDs"""
     ISTANBUL = 1
-    # Add more as discovered
+    ANKARA = 2
+    IZMIR = 3
+    # Add more as discovered

138-161: Test code logs potentially sensitive applicant names.

The __main__ block prints applicant first and last names. While useful for debugging, this could inadvertently expose PII in logs if run with real data.

Consider masking or truncating sensitive fields in debug output:

     print("\nApplicant Config:")
-    print(f"  Name: {config['applicant'].first_name} {config['applicant'].last_name}")
+    print(f"  Name configured: {bool(config['applicant'].first_name and config['applicant'].last_name)}")
     print(f"  Valid: {config['applicant'].is_valid()}")
reservation_bot.py (2)

196-210: Unused variable response.

The response variable is assigned but never used. Either use it or remove the assignment.

🔎 Proposed fix
         try:
             # This endpoint is theoretical
-            response = self.session.get(
+            self.session.get(
                 f"{self.BOOKING_PORTAL}/",
                 impersonate="chrome"
             )

84-97: Mutable class attribute DEFAULT_HEADERS should use ClassVar.

As noted by static analysis, mutable class attributes should be annotated with typing.ClassVar to indicate they are shared across instances.

🔎 Proposed fix
+from typing import ClassVar
+
 class KosmosReservationBot:
     # ...
     
     # Common headers to mimic browser
-    DEFAULT_HEADERS = {
+    DEFAULT_HEADERS: ClassVar[dict] = {
         'accept': 'application/json',
         # ...
     }
hybrid_bot.py (3)

706-730: Multiple bare except clauses silently swallow all errors.

The selector iteration loops use bare except: pass which hides failures including programming errors. At minimum, log these failures at debug level.

🔎 Proposed fix for one instance (apply pattern to others)
         # Try to select dealer
         for selector in selectors['dealer']:
             try:
                 if await self.page.locator(selector).count() > 0:
                     await self.page.select_option(selector, str(self.config.dealer_id))
                     break
-            except:
-                pass
+            except Exception as e:
+                self.logger.debug(f"Selector {selector} failed: {e}")

491-498: f-strings without placeholders are unnecessary.

Lines 493-496 use f-strings but contain no interpolated values. Remove the f prefix.

🔎 Proposed fix
     def notify_captcha_required(self):
         """Notify that CAPTCHA needs manual solving"""
         message = (
-            f"⚠️ <b>CAPTCHA DETECTED!</b>\n\n"
-            f"Please solve the CAPTCHA manually.\n"
-            f"The browser window should be visible.\n\n"
-            f"⏱ Waiting up to 2 minutes..."
+            "⚠️ <b>CAPTCHA DETECTED!</b>\n\n"
+            "Please solve the CAPTCHA manually.\n"
+            "The browser window should be visible.\n\n"
+            "⏱ Waiting up to 2 minutes..."
         )
         self.send(message)

240-240: Use Optional[str] instead of implicit None default.

PEP 484 prohibits implicit Optional. The error parameter should be explicitly typed.

🔎 Proposed fix
-    def log_booking_attempt(self, slot_key: str, status: str, error: str = None):
+    def log_booking_attempt(self, slot_key: str, status: str, error: Optional[str] = None):
Dockerfile (2)

1-49: Container runs as root user - security concern.

Running containers as root increases the attack surface. Consider adding a non-root user.

🔎 Proposed fix
 # Create directories for data persistence
 RUN mkdir -p /app/screenshots /app/data

+# Create non-root user
+RUN useradd --create-home --shell /bin/bash botuser \
+    && chown -R botuser:botuser /app
+
+USER botuser
+
 # Environment variables
 ENV PYTHONUNBUFFERED=1

 # Default command
 CMD ["python", "hybrid_bot.py"]

38-40: Glob pattern *.py may copy unintended files.

Using COPY *.py . copies all Python files from the build context. If there are test files or other scripts not needed in production, they'll be included. Consider being explicit or using .dockerignore.

Either list files explicitly:

-COPY *.py .
+COPY browser_bot.py config.py hybrid_bot.py reservation_bot.py .

Or create a .dockerignore file to exclude test files and other non-production code.

docker-compose.yml (1)

1-1: Remove the version key from docker-compose.yml.

Docker Compose V2 ignores the version key and may produce a warning when present. The Compose Specification does not require this key, so it can safely be removed.

🔎 Proposed fix
-version: '3.8'
-
 services:
   kosmos-bot:
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cbd8c89 and 36f3077.

📒 Files selected for processing (9)
  • .env.example
  • Dockerfile
  • browser_bot.py
  • config.py
  • docker-compose.yml
  • hybrid_bot.py
  • readme.md
  • requirements.txt
  • reservation_bot.py
🧰 Additional context used
🧬 Code graph analysis (1)
browser_bot.py (2)
config.py (2)
  • ApplicationType (17-26)
  • AppointmentTypeId (29-39)
reservation_bot.py (6)
  • ApplicationType (32-34)
  • AppointmentTypeId (37-40)
  • ApplicantInfo (51-61)
  • send_notification (330-346)
  • run (348-428)
  • main (458-489)
🪛 dotenv-linter (4.0.0)
.env.example

[warning] 22-22: [UnorderedKey] The APPLICANT_BIRTH_DATE key should go before the APPLICANT_FIRST_NAME key

(UnorderedKey)


[warning] 23-23: [UnorderedKey] The APPLICANT_EMAIL key should go before the APPLICANT_FIRST_NAME key

(UnorderedKey)

🪛 LanguageTool
readme.md

[style] ~85-~85: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...tate persistence | ✅ SQLite | ❌ | ❌ | | CLI interface | ✅ | ❌ | ❌ | | Auto-booking | ✅ | ❌ | ...

(ACRONYM_TAUTOLOGY)

🪛 markdownlint-cli2 (0.18.1)
readme.md

20-20: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


92-92: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


222-222: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🪛 Ruff (0.14.10)
reservation_bot.py

84-97: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


160-160: Consider moving this statement to an else block

(TRY300)


162-162: Do not catch blind exception: Exception

(BLE001)


163-163: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


198-198: Local variable response is assigned to but never used

Remove assignment to unused variable response

(F841)


210-210: Consider moving this statement to an else block

(TRY300)


212-212: Do not catch blind exception: Exception

(BLE001)


213-213: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


326-326: Do not catch blind exception: Exception

(BLE001)


327-327: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


345-345: Do not catch blind exception: Exception

(BLE001)


346-346: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


426-426: Do not catch blind exception: Exception

(BLE001)


427-427: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

hybrid_bot.py

240-240: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)


300-312: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


368-368: Consider moving this statement to an else block

(TRY300)


370-370: Do not catch blind exception: Exception

(BLE001)


371-371: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


466-466: Do not catch blind exception: Exception

(BLE001)


467-467: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


493-493: f-string without any placeholders

Remove extraneous f prefix

(F541)


494-494: f-string without any placeholders

Remove extraneous f prefix

(F541)


495-495: f-string without any placeholders

Remove extraneous f prefix

(F541)


496-496: f-string without any placeholders

Remove extraneous f prefix

(F541)


565-568: Avoid specifying long messages outside the exception class

(TRY003)


660-660: Abstract raise to an inner function

(TRY301)


660-660: Create your own exception

(TRY002)


660-660: Avoid specifying long messages outside the exception class

(TRY003)


673-673: Abstract raise to an inner function

(TRY301)


673-673: Create your own exception

(TRY002)


673-673: Avoid specifying long messages outside the exception class

(TRY003)


675-675: Do not catch blind exception: Exception

(BLE001)


677-677: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


711-711: Do not use bare except

(E722)


711-712: try-except-pass detected, consider logging the exception

(S110)


720-720: Do not use bare except

(E722)


720-721: try-except-pass detected, consider logging the exception

(S110)


729-729: Do not use bare except

(E722)


729-730: try-except-pass detected, consider logging the exception

(S110)


766-766: Do not use bare except

(E722)


766-767: try-except-pass detected, consider logging the exception

(S110)


816-816: Consider moving this statement to an else block

(TRY300)


817-817: Do not use bare except

(E722)


818-818: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


844-844: Do not use bare except

(E722)


844-845: try-except-pass detected, consider logging the exception

(S110)


872-872: Do not use bare except

(E722)


872-873: try-except-pass detected, consider logging the exception

(S110)


882-882: String contains ambiguous ı (LATIN SMALL LETTER DOTLESS I). Did you mean i (LATIN SMALL LETTER I)?

(RUF001)


882-882: String contains ambiguous ı (LATIN SMALL LETTER DOTLESS I). Did you mean i (LATIN SMALL LETTER I)?

(RUF001)


940-940: Unused function argument: sig

(ARG001)


940-940: Unused function argument: frame

(ARG001)


980-980: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1026-1026: Do not catch blind exception: Exception

(BLE001)


1027-1027: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

browser_bot.py

101-101: Avoid specifying long messages outside the exception class

(TRY003)


186-186: Do not catch blind exception: Exception

(BLE001)


187-187: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


255-255: Do not catch blind exception: Exception

(BLE001)


256-256: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


287-287: Do not catch blind exception: Exception

(BLE001)


288-288: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


324-324: Do not catch blind exception: Exception

(BLE001)


363-363: Consider moving this statement to an else block

(TRY300)


364-364: Do not use bare except

(E722)


365-365: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


408-408: String contains ambiguous ı (LATIN SMALL LETTER DOTLESS I). Did you mean i (LATIN SMALL LETTER I)?

(RUF001)


408-408: String contains ambiguous ı (LATIN SMALL LETTER DOTLESS I). Did you mean i (LATIN SMALL LETTER I)?

(RUF001)


438-438: Consider moving this statement to an else block

(TRY300)


440-440: Do not catch blind exception: Exception

(BLE001)


441-441: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


481-481: Consider moving this statement to an else block

(TRY300)


483-483: Do not catch blind exception: Exception

(BLE001)


484-484: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


501-501: Do not catch blind exception: Exception

(BLE001)


502-502: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

🔇 Additional comments (2)
.env.example (1)

1-74: Configuration template is comprehensive and well-documented.

Clear sections with helpful comments. Example values are realistic (e.g., appointment types, time formats) and defaults are safe (AUTO_BOOK=false).

Recommend verifying that all variables in this template are actually referenced by the configuration module (src/config.py) and that none are missing. If the config module accepts additional env vars beyond what's listed here, consider documenting them.

readme.md (1)

1-236: Documentation is comprehensive, well-organized, and accurate.

The README clearly explains the bot architecture, configuration, deployment, and includes practical examples. Feature descriptions align with the PR objectives (monitoring, Telegram alerts, auto-booking via browser automation, state persistence, Docker support).

Verify that references to bot modules (hybrid_bot.py, browser_bot.py, reservation_bot.py) exist and that the architecture diagram accurately reflects the implementation before merging.

Comment on lines +39 to +47
class ApplicationType(Enum):
INDIVIDUAL = 1
FAMILY = 2


class AppointmentTypeId(Enum):
STANDARD = 16
VIP = 18
EEA_AB_SPOUSE = 2339
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Duplicate enum definitions - import from config.py instead.

ApplicationType and AppointmentTypeId are already defined in config.py with additional helper methods (from_value). This duplication will cause maintenance issues and potential inconsistencies.

🔎 Proposed fix
-class ApplicationType(Enum):
-    INDIVIDUAL = 1
-    FAMILY = 2
-
-
-class AppointmentTypeId(Enum):
-    STANDARD = 16
-    VIP = 18
-    EEA_AB_SPOUSE = 2339
+from config import ApplicationType, AppointmentTypeId

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In browser_bot.py around lines 39 to 47, the enums ApplicationType and
AppointmentTypeId are duplicated here but already defined in config.py
(including helper methods like from_value); remove these local enum definitions,
import ApplicationType and AppointmentTypeId from config.py at the top of the
file, update any local references to use the imported names (and use the
from_value helper where appropriate), and clean up any now-unused imports (e.g.,
Enum) to avoid duplication and inconsistencies.

Comment on lines +98 to +149
async def setup_browser(self):
"""Initialize the browser with anti-detection settings"""
if not PLAYWRIGHT_AVAILABLE:
raise RuntimeError("Playwright is not installed")

logger.info("Setting up browser...")

playwright = await async_playwright().start()

# Launch browser with stealth settings
self.browser = await playwright.chromium.launch(
headless=self.config.headless,
args=[
'--disable-blink-features=AutomationControlled',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
]
)

# Create context with realistic viewport and user agent
self.context = await self.browser.new_context(
viewport={'width': 1920, 'height': 1080},
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
locale='tr-TR',
timezone_id='Europe/Istanbul',
)

# Add stealth scripts to avoid detection
await self.context.add_init_script("""
// Override navigator.webdriver
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});

// Override plugins
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
});

// Override languages
Object.defineProperty(navigator, 'languages', {
get: () => ['tr-TR', 'tr', 'en-US', 'en']
});
""")

self.page = await self.context.new_page()

# Set default timeout
self.page.set_default_timeout(30000)

logger.info("Browser setup complete")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Playwright instance resource leak.

The playwright instance created at line 105 is never stored or properly closed. When close_browser() is called, it only closes the browser but not the Playwright instance, leading to resource leaks.

🔎 Proposed fix
 def __init__(self, applicant: ApplicantInfo, config: BookingConfig):
     self.applicant = applicant
     self.config = config
     self.browser: Optional[Browser] = None
     self.context: Optional[BrowserContext] = None
     self.page: Optional[Page] = None
     self.notified_slots: set = set()
+    self._playwright = None

 async def setup_browser(self):
     """Initialize the browser with anti-detection settings"""
     if not PLAYWRIGHT_AVAILABLE:
         raise RuntimeError("Playwright is not installed")

     logger.info("Setting up browser...")

-    playwright = await async_playwright().start()
+    self._playwright = await async_playwright().start()

     # Launch browser with stealth settings
-    self.browser = await playwright.chromium.launch(
+    self.browser = await self._playwright.chromium.launch(
 async def close_browser(self):
     """Clean up browser resources"""
     if self.browser:
         await self.browser.close()
+    if self._playwright:
+        await self._playwright.stop()
     logger.info("Browser closed")

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.10)

101-101: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In browser_bot.py around lines 98 to 149, the Playwright instance created with
async_playwright().start() is not stored or stopped, causing a resource leak;
update setup_browser to assign the returned object to an instance attribute
(e.g. self.playwright) and ensure any cleanup path knows about it, then modify
close_browser to await self.playwright.stop() (after or before closing the
browser/context) and guard with checks so stop is only called if the instance
exists; also ensure exceptions during setup properly close any partially-created
resources by using try/except/finally or async context patterns to stop the
Playwright instance on failure.

Comment on lines +210 to +221
async def navigate_to_booking(self):
"""Navigate to the booking portal"""
logger.info(f"Navigating to {self.BOOKING_URL}")

await self.page.goto(self.BOOKING_URL, wait_until='networkidle')

# Wait for page to fully load
await self.page.wait_for_load_state('domcontentloaded')

# Take screenshot for debugging
await self.page.screenshot(path='screenshots/booking_page.png')

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Screenshot path may fail if directory doesn't exist.

navigate_to_booking writes to screenshots/ but the directory is only created in run() at line 517. If navigate_to_booking is called directly (e.g., from attempt_booking before run()), this will fail.

🔎 Proposed fix - create directory in `setup_browser`
 async def setup_browser(self):
     """Initialize the browser with anti-detection settings"""
     if not PLAYWRIGHT_AVAILABLE:
         raise RuntimeError("Playwright is not installed")

     logger.info("Setting up browser...")
+
+    # Ensure screenshots directory exists
+    os.makedirs('screenshots', exist_ok=True)

     playwright = await async_playwright().start()

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +362 to +366
logger.info("CAPTCHA solved!")
return True
except:
logger.error("CAPTCHA solving timeout")
return False
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Bare except swallows all exceptions including KeyboardInterrupt and SystemExit.

Use a specific exception type to avoid masking critical errors.

🔎 Proposed fix
                     try:
                         await self.page.wait_for_selector(
                             '.recaptcha-success, [data-captcha-solved="true"]',
                             timeout=120000  # 2 minutes
                         )
                         logger.info("CAPTCHA solved!")
                         return True
-                    except:
+                    except Exception:
                         logger.error("CAPTCHA solving timeout")
                         return False
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info("CAPTCHA solved!")
return True
except:
logger.error("CAPTCHA solving timeout")
return False
logger.info("CAPTCHA solved!")
return True
except Exception:
logger.error("CAPTCHA solving timeout")
return False
🧰 Tools
🪛 Ruff (0.14.10)

363-363: Consider moving this statement to an else block

(TRY300)


364-364: Do not use bare except

(E722)


365-365: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

🤖 Prompt for AI Agents
In browser_bot.py around lines 362-366, the bare except is swallowing all
exceptions; change it to catch a specific timeout-related exception (e.g.,
TimeoutException from the library you use) or at minimum catch Exception as e,
log the exception details (logger.exception or logger.error with e) and re-raise
critical exceptions if needed (ensure KeyboardInterrupt/SystemExit are not
caught). Update imports for the chosen exception and replace the bare except
block with the specific/Exception handler that logs the error and returns False.

Comment on lines +85 to +91
dealer_id: int = field(default_factory=lambda: int(os.getenv('DEALER_ID', '1')))
application_type: ApplicationType = field(
default_factory=lambda: ApplicationType.from_value(int(os.getenv('APPLICATION_TYPE', '1')))
)
appointment_type: AppointmentTypeId = field(
default_factory=lambda: AppointmentTypeId.from_value(int(os.getenv('APPOINTMENT_TYPE', '16')))
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing error handling for integer parsing from environment variables.

If DEALER_ID, APPLICATION_TYPE, APPOINTMENT_TYPE, CHECK_INTERVAL, or DAYS_TO_CHECK contain non-integer values, a ValueError will crash the application during config loading.

🔎 Proposed fix - add safe parsing with defaults
+def _safe_int(value: str, default: int) -> int:
+    """Safely parse integer from string, returning default on failure"""
+    try:
+        return int(value)
+    except (ValueError, TypeError):
+        return default
+
+
 @dataclass
 class BotConfig:
     """Main bot configuration"""
     # Appointment settings
-    dealer_id: int = field(default_factory=lambda: int(os.getenv('DEALER_ID', '1')))
+    dealer_id: int = field(default_factory=lambda: _safe_int(os.getenv('DEALER_ID', '1'), 1))
     application_type: ApplicationType = field(
-        default_factory=lambda: ApplicationType.from_value(int(os.getenv('APPLICATION_TYPE', '1')))
+        default_factory=lambda: ApplicationType.from_value(_safe_int(os.getenv('APPLICATION_TYPE', '1'), 1))
     )
     appointment_type: AppointmentTypeId = field(
-        default_factory=lambda: AppointmentTypeId.from_value(int(os.getenv('APPOINTMENT_TYPE', '16')))
+        default_factory=lambda: AppointmentTypeId.from_value(_safe_int(os.getenv('APPOINTMENT_TYPE', '16'), 16))
     )
🤖 Prompt for AI Agents
In config.py around lines 85 to 91, environment integer parsing (DEALER_ID,
APPLICATION_TYPE, APPOINTMENT_TYPE and also CHECK_INTERVAL, DAYS_TO_CHECK
elsewhere) is done directly with int(...) which will raise ValueError for
non-integer env values; change this to use a small safe parsing routine or
inline try/except that attempts to int(os.getenv(...)) and falls back to the
intended default on failure, then pass the validated integer into
ApplicationType.from_value and AppointmentTypeId.from_value (and into field
defaults for CHECK_INTERVAL/DAYS_TO_CHECK) so invalid env content won't crash
config loading.

Comment on lines +330 to +342
try:
# Run in executor to avoid blocking
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(
None,
lambda: curl_requests.get(
self.API_URL,
headers=self.HEADERS,
params=params,
impersonate="chrome",
timeout=10
)
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Deprecated asyncio.get_event_loop() usage.

asyncio.get_event_loop() is deprecated in Python 3.10+ when called from a coroutine. Use asyncio.get_running_loop() instead.

🔎 Proposed fix
         try:
             # Run in executor to avoid blocking
-            loop = asyncio.get_event_loop()
+            loop = asyncio.get_running_loop()
             response = await loop.run_in_executor(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
# Run in executor to avoid blocking
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(
None,
lambda: curl_requests.get(
self.API_URL,
headers=self.HEADERS,
params=params,
impersonate="chrome",
timeout=10
)
)
try:
# Run in executor to avoid blocking
loop = asyncio.get_running_loop()
response = await loop.run_in_executor(
None,
lambda: curl_requests.get(
self.API_URL,
headers=self.HEADERS,
params=params,
impersonate="chrome",
timeout=10
)
)
🤖 Prompt for AI Agents
In hybrid_bot.py around lines 330 to 342, the coroutine is calling the
deprecated asyncio.get_event_loop(); replace it with asyncio.get_running_loop()
to obtain the current loop from within a coroutine, e.g., call loop =
asyncio.get_running_loop() before run_in_executor, and keep the rest of the
run_in_executor call unchanged so execution remains off the event loop; no
additional imports are required.

Comment on lines +417 to +427
def _filter_by_time(self, slots: List[Slot]) -> List[Slot]:
"""Filter slots by preferred time ranges"""
filtered = []
for slot in slots:
for time_range in self.config.preferred_times:
if '-' in time_range:
start, end = time_range.split('-')
if start.strip() <= slot.time <= end.strip():
filtered.append(slot)
break
return filtered
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same string-based time comparison issue as in reservation_bot.py.

The time filtering logic uses string comparison which may fail for non-zero-padded times.

Apply the same fix as suggested for reservation_bot.py - parse times properly before comparison.

🤖 Prompt for AI Agents
In hybrid_bot.py around lines 417 to 427, the time-range filter currently
compares time strings which fails for non-zero-padded times; update it to parse
both the slot.time and each range endpoint into a comparable time value (e.g.,
datetime.time via datetime.strptime or integer minutes since midnight) before
comparing, trim whitespace, handle ranges that may cross midnight by treating
end < start as spanning midnight (compare accordingly), and only append the slot
when the parsed slot time falls within the parsed range.

Comment on lines +1 to +3
curl_cffi>=0.5.0
python-dotenv>=1.0.0
playwright>=1.40.0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | head -20

Repository: ismkdc/kosmos-vize-bot

Length of output: 200


🏁 Script executed:

# Find all Python files to understand project structure
fd -e py | head -20

Repository: ismkdc/kosmos-vize-bot

Length of output: 130


🏁 Script executed:

# Search for imports of the three dependencies to understand usage
rg "import (curl_cffi|python-dotenv|playwright)" -t py

Repository: ismkdc/kosmos-vize-bot

Length of output: 48


🏁 Script executed:

# Also check for 'from' imports
rg "from (curl_cffi|python-dotenv|playwright)" -t py

Repository: ismkdc/kosmos-vize-bot

Length of output: 460


🏁 Script executed:

# Check if there's any version pinning documentation or constraints elsewhere
fd -e txt -e md -e toml -e cfg | xargs rg "playwright|curl_cffi|python-dotenv" 2>/dev/null

Repository: ismkdc/kosmos-vize-bot

Length of output: 240


🌐 Web query:

Playwright 1.40.0 to latest version breaking changes browser automation

💡 Result:

Here are the main breaking changes in Playwright from v1.40.0 onward that can affect browser automation (summary — check the linked release notes for details and exact versions):

  • Playwright no longer automatically downloads browser binaries for the standalone packages; use npx playwright install (affects automation scripts that relied on auto-download). (playwright.dev)
  • playwright-core binary/name changes (playwright-core binary renamed / CLI behavior changed). Update any scripts that call the old CLI. (playwright.dev)
  • route/page.route-related changes: glob URL patterns no longer support ? and []; route.continue() can no longer override the Cookie header (cookies come from the browser store). Adjust route matching and request-modification code. (playwright.dev)
  • Events and APIs removed/deprecated impacting automation: browserContext.on('backgroundpage') deprecated; some event behaviors changed (use backgroundPages() etc.). (playwright.dev)
  • Input/typing and locator API changes: page.type()/frame.type()/locator.type() were deprecated in favor of locator.fill() / locator.pressSequentially(); some expect/locator assertions tightened (e.g., toBeEditable now throws for non-editable elements). Review tests/scripts using deprecated input APIs and assertions. (playwright.dev)

For a complete, version-by-version list (and browser engine versions), see the Playwright release notes. (playwright.dev)

If you want, I can: (a) produce a patch list of code changes for your repository (need a sample of your tests/scripts), or (b) extract the exact breaking items per intermediate release between 1.40.0 and the current latest. Which would you prefer?

Citations:


🌐 Web query:

curl_cffi version compatibility and breaking changes

💡 Result:

Summary of curl_cffi compatibility and notable breaking changes

  • Python support:

    • Minimum Python raised over time (was 3.8 in v0.6; minimum is Python 3.9 since v0.10). [1][4]
  • libcurl / protocol support:

    • Bundled libcurl (curl-impersonate) has been upgraded multiple times (e.g., to 8.1.1 in v0.6 and 8.7.1 in v0.7) and HTTP/3 was added in v0.11 — changes in bundled libcurl can affect TLS/HTTP behaviors and platform binary requirements. [2][4]
  • API / behavioral changes you may hit when upgrading:

    • AsyncSession.close became an async coroutine (v0.6.1) — code calling it synchronously must be updated. [4]
    • Default redirect limit changed (v0.7) — may alter redirect behavior. [3]
    • Header strategy changed: empty headers excluded and Expect header no longer sent by default (v0.7). [3]
    • Some Windows fingerprint fixes and platform build-target changes across versions (v0.6 → v0.9). [4][2]
  • Fingerprints / impersonation:

    • Browser impersonation targets are added/changed frequently; some recent targets (and some commercial-only targets) may be present or omitted between releases — if you rely on a specific fingerprint name, verify it exists in your curl_cffi version or provide custom JA3/headers. [1][2]
  • Packaging / wheels:

    • Manylinux/macOS/Win wheel targets expanded; prebuilt wheels include bundled libcurl-impersonate versions — upgrading may change system/GLIBC requirements for the wheel. Check the wheel tags on PyPI for platform/glibc requirements before upgrading. [1]

Where to check details before upgrading

  • Official changelog (ReadTheDocs) for version-by-version notes. [3]
  • PyPI release history / wheel metadata for platform compatibility. [1]
  • GitHub Releases for finer breaking-change descriptions and migration notes. [2]

Sources: PyPI release page and project docs/changelog. [1][2][3][4]


🌐 Web query:

python-dotenv version 1.0.0 stability and API changes

💡 Result:

Summary: python-dotenv 1.0.0 (released Feb 24, 2023) is a stable 1.0 "major" release; it did not introduce large API removals but made Python-compatibility changes and small bug fixes.

Key points

  • Release/date: 1.0.0 — 2023-02-24. [1]
  • Stability: marked as a production/stable 1.0 release (semantic-versioning); subsequent patch releases (1.0.1 etc.) fixed edge cases. [1][2]
  • Notable changes in 1.0.0:
    • Dropped support for Python 3.7 and added support for Python 3.12-dev (compatibility change, not an API removal). [1][2]
    • Fixed handling for cases where the current working directory does not exist. [1][2]
  • API surface: no documented breaking API removals in the 1.0.0 changelog — typical usage (load_dotenv, dotenv_values, set_key/get_key/unset_key, CLI) continued to work; most later changes have been fixes, new Python-version support, or CLI/behavior improvements in minor/patch releases. [1][2]

If you want, I can:

  • list any small behavioral changes in functions (load_dotenv, dotenv_values, set_key, etc.) across 0.21.x → 1.0.0 and 1.0.0 → 1.0.1, with exact changelog lines and links.

Sources
[1] python-dotenv changelog / PyPI release notes (1.0.0 entry).
[2] Project changelog mirror (detailed entries for 1.0.0 and later).


Tighten dependency version constraints to prevent breaking changes in automated deployments.

Breaking changes confirmed in Playwright (1.40.0+) and curl_cffi (0.5.0+) could affect bot stability. Playwright has deprecated input APIs (page.type() → locator.fill()), removed browser auto-download, and changed route/assertion behaviors. curl_cffi has altered AsyncSession.close() to async-only (v0.6.1+), changed redirect handling (v0.7+), and shifted header strategies.

Use upper-bound constraints to allow patch updates while preventing breaking minor/major releases:

Recommended version constraints
curl_cffi>=0.5.0,<0.10.0
python-dotenv>=1.0.0,<2.0.0
playwright>=1.40.0,<2.0.0

(Adjust upper bounds based on your tested compatibility; python-dotenv is stable, but curl_cffi and playwright warrant tighter control.)

🤖 Prompt for AI Agents
In requirements.txt around lines 1 to 3, the current unconstrained
lower-bound-only pins allow breaking minor/major releases for curl_cffi and
playwright; update each dependency to include an appropriate upper-bound to
prevent unexpected breaking changes (for example constrain curl_cffi and
playwright to a safe upper bound such as <0.10.0 and <2.0.0 respectively, and
pin python-dotenv to <2.0.0 or another tested upper bound). Edit the three lines
to use range constraints (>=... , <...) per the recommended bounds or your
tested compatibility, so patch updates remain allowed but minor/major breaking
releases are excluded.

Comment on lines +32 to +48
class ApplicationType(Enum):
INDIVIDUAL = 1 # Bireysel
FAMILY = 2 # Aile


class AppointmentTypeId(Enum):
STANDARD = 16 # Standart
VIP = 18 # Vip
EEA_AB_SPOUSE = 2339 # EEA AB Eşi


class DealerId(Enum):
"""Dealer/Location IDs - These are theoretical values"""
ISTANBUL = 1
ANKARA = 2
IZMIR = 3

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Duplicate enum definitions across multiple files.

ApplicationType, AppointmentTypeId, and DealerId are defined here, in config.py, and in browser_bot.py. This creates maintenance burden and risk of inconsistency.

Consolidate all enum definitions in config.py and import them in other modules:

-class ApplicationType(Enum):
-    INDIVIDUAL = 1  # Bireysel
-    FAMILY = 2      # Aile
-
-
-class AppointmentTypeId(Enum):
-    STANDARD = 16       # Standart
-    VIP = 18            # Vip
-    EEA_AB_SPOUSE = 2339  # EEA AB Eşi
-
-
-class DealerId(Enum):
-    """Dealer/Location IDs - These are theoretical values"""
-    ISTANBUL = 1
-    ANKARA = 2
-    IZMIR = 3
+from config import ApplicationType, AppointmentTypeId, DealerId
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class ApplicationType(Enum):
INDIVIDUAL = 1 # Bireysel
FAMILY = 2 # Aile
class AppointmentTypeId(Enum):
STANDARD = 16 # Standart
VIP = 18 # Vip
EEA_AB_SPOUSE = 2339 # EEA AB Eşi
class DealerId(Enum):
"""Dealer/Location IDs - These are theoretical values"""
ISTANBUL = 1
ANKARA = 2
IZMIR = 3
from config import ApplicationType, AppointmentTypeId, DealerId
🤖 Prompt for AI Agents
In reservation_bot.py around lines 32 to 48 you define ApplicationType,
AppointmentTypeId, and DealerId which are duplicated in config.py and
browser_bot.py; remove these local enum definitions and instead import the enums
from config.py (e.g., from config import ApplicationType, AppointmentTypeId,
DealerId), update any local references to use the imported names, delete the
duplicate enums from reservation_bot.py, and then remove or replace the
duplicate definitions in other modules so all three enums live only in
config.py; run tests / linter to ensure no unresolved imports remain.

Comment on lines +445 to +453
def time_in_range(slot_time: str, ranges: List[str]) -> bool:
for time_range in ranges:
if '-' in time_range:
start, end = time_range.split('-')
if start <= slot_time <= end:
return True
return False

filtered = [s for s in filtered if time_in_range(s.time, preferred_times)]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

String-based time comparison may produce incorrect results.

Comparing times as strings (e.g., start <= slot_time <= end) only works correctly if times are zero-padded (e.g., "09:00" vs "9:00"). Times like "9:00" would incorrectly compare greater than "10:00".

🔎 Proposed fix - parse times properly
 def time_in_range(slot_time: str, ranges: List[str]) -> bool:
+    from datetime import datetime
+    def parse_time(t: str) -> datetime:
+        return datetime.strptime(t.strip(), "%H:%M")
+    
     for time_range in ranges:
         if '-' in time_range:
             start, end = time_range.split('-')
-            if start <= slot_time <= end:
+            try:
+                slot_t = parse_time(slot_time)
+                if parse_time(start) <= slot_t <= parse_time(end):
+                    return True
+            except ValueError:
+                continue
-                return True
     return False
🤖 Prompt for AI Agents
In reservation_bot.py around lines 445 to 453, the helper time_in_range
currently compares times as strings which fails for non-zero-padded times;
change it to parse times into a comparable numeric form (e.g., use
datetime.strptime to parse "H:M" or "HH:MM" and compare datetime.time objects,
or convert hours and minutes into total minutes) then perform the comparisons;
ensure you handle invalid formats with a safe fallback and trim whitespace when
splitting ranges; finally use the parsed comparison in the filtered list
comprehension.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants