Created
February 2, 2026 14:29
-
-
Save azer/5d8220b4dff27b89fa08acd884761916 to your computer and use it in GitHub Desktop.
Automate BMS school lunch orders
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 0 9 * * 6 cd /home/fuji/dev/lunch/run.sh |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| School Lunch Ordering Script using browser-use | |
| ============================================== | |
| This script: | |
| 1. Logs into the school lunch website | |
| 2. CHECKS if meals are already ordered for next 2 weeks | |
| 3. Only orders if needed | |
| 4. Sends Telegram notification with status | |
| Install dependencies: | |
| pip install browser-use python-dotenv aiohttp | |
| """ | |
| import asyncio | |
| import os | |
| from datetime import datetime | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| # ============================================ | |
| # CONFIGURATION | |
| # ============================================ | |
| CUSTOMER_ID = os.getenv("LUNCH_CUSTOMER_ID", "") | |
| PASSWORD = os.getenv("LUNCH_PASSWORD", "") | |
| TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "") | |
| TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "") | |
| LOGIN_URL = "https://widynski-roick.mbs5online.de/ordering/" | |
| DIET_PREFERENCES = """ | |
| Diet rules for selecting lunch: | |
| 1. NEVER select any dish containing pork (Schwein, Schweinefleisch, Schinken, Speck, Bacon, Wurst unless specified as chicken/beef) | |
| 2. PREFER simple dishes like: | |
| - Pasta dishes (Nudeln, Spaghetti, Penne, Makkaroni) | |
| - Rice dishes (Reis) | |
| - Chicken (Hähnchen, Huhn, Geflügel) | |
| - Fish sticks (Fischstäbchen) | |
| - Pizza | |
| - Schnitzel (only if chicken/turkey, NOT pork) | |
| - Pancakes (Pfannkuchen, Eierkuchen) | |
| 3. AVOID dishes with too many vegetables as main component | |
| 4. If multiple options available, choose the simpler/kid-friendlier option | |
| 5. German menu items are common - translate and apply rules accordingly | |
| """ | |
| # ============================================ | |
| # TELEGRAM NOTIFICATION | |
| # ============================================ | |
| async def send_telegram_notification(message: str): | |
| """Send notification via Telegram""" | |
| if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID: | |
| print("Telegram not configured, skipping notification") | |
| return False | |
| import aiohttp | |
| url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage" | |
| payload = { | |
| "chat_id": TELEGRAM_CHAT_ID, | |
| "text": message, | |
| "parse_mode": "HTML" | |
| } | |
| try: | |
| async with aiohttp.ClientSession() as session: | |
| async with session.post(url, json=payload) as response: | |
| if response.status == 200: | |
| print("✅ Telegram notification sent!") | |
| return True | |
| else: | |
| print(f"❌ Telegram error: {response.status}") | |
| return False | |
| except Exception as e: | |
| print(f"❌ Telegram error: {e}") | |
| return False | |
| # ============================================ | |
| # MAIN AGENT TASK | |
| # ============================================ | |
| async def order_school_lunch(): | |
| """Main function to order school lunch using browser-use""" | |
| from browser_use import Agent, Browser, ChatBrowserUse | |
| llm = ChatBrowserUse() | |
| print("Using ChatBrowserUse LLM") | |
| browser = Browser(use_cloud=True) | |
| print("Using Browser-Use Cloud browser") | |
| # Task now includes checking existing orders first | |
| task = f""" | |
| Your task is to check and order school lunch for Fuji for the next two weeks. | |
| STEP 1 - LOGIN: | |
| 1. Go to: {LOGIN_URL} | |
| 2. Find the login form | |
| 3. Enter customer number/ID: {CUSTOMER_ID} | |
| 4. Enter password: {PASSWORD} | |
| 5. Click login/submit button | |
| STEP 2 - CHECK EXISTING ORDERS: | |
| 1. After login, navigate to the ordering/menu section | |
| 2. Look at the next two weeks | |
| 3. CHECK if meals are ALREADY ORDERED for each day | |
| 4. Note which days already have orders and what meals are selected | |
| 5. Note which days are MISSING orders (need to be ordered) | |
| STEP 3 - DETERMINE ACTION: | |
| - If ALL days for the next 2 weeks already have meals ordered: | |
| → Report "All meals already ordered" with the list of what's ordered | |
| → Skip to STEP 5 (do not order anything) | |
| - If SOME days are missing orders: | |
| → Report which days are already ordered | |
| → Proceed to STEP 4 to order the missing days | |
| STEP 4 - SELECT MEALS (only for days without orders): | |
| For each day that NEEDS ordering, select a meal following these diet rules: | |
| {DIET_PREFERENCES} | |
| After selecting, confirm/submit the order. | |
| STEP 5 - FINAL REPORT: | |
| Provide a clear summary with: | |
| STATUS: [ALREADY_ORDERED / NEWLY_ORDERED / PARTIAL_ORDER] | |
| ALREADY ORDERED (were already in the system): | |
| - List each date and meal that was already ordered | |
| NEWLY ORDERED (ordered during this session): | |
| - List each date and meal that was just ordered | |
| - Or "None" if everything was already ordered | |
| DAYS WITHOUT ORDERS: | |
| - List any days that couldn't be ordered (if any) | |
| - Or "None" if all days are covered | |
| IMPORTANT: | |
| - Be thorough when checking existing orders | |
| - Do NOT re-order days that already have meals selected | |
| - If unsure about a dish containing pork, skip it | |
| """ | |
| agent = Agent( | |
| task=task, | |
| llm=llm, | |
| browser=browser, | |
| ) | |
| print("=" * 60) | |
| print("🍽️ Starting School Lunch Ordering Agent") | |
| print("=" * 60) | |
| print("\nStep 1: Check existing orders") | |
| print("Step 2: Order only if needed\n") | |
| try: | |
| history = await agent.run(max_steps=100) | |
| result = history.final_result() if history.final_result() else "Task completed" | |
| print("\n" + "=" * 60) | |
| print("📋 RESULT") | |
| print("=" * 60) | |
| print(result) | |
| # Determine status for Telegram | |
| result_lower = str(result).lower() | |
| if "already ordered" in result_lower or "already_ordered" in result_lower: | |
| status_emoji = "✅" | |
| status_text = "Already Ordered" | |
| elif "newly ordered" in result_lower or "newly_ordered" in result_lower: | |
| status_emoji = "🆕" | |
| status_text = "New Orders Placed" | |
| else: | |
| status_emoji = "📋" | |
| status_text = "Completed" | |
| notification_message = f""" | |
| 🍽️ <b>School Lunch Order Check</b> | |
| Child: Fuji | |
| Status: {status_emoji} {status_text} | |
| <b>Details:</b> | |
| {str(result)[:800]} | |
| <i>Checked at: {datetime.now().strftime('%Y-%m-%d %H:%M')}</i> | |
| """ | |
| await send_telegram_notification(notification_message) | |
| return result | |
| except Exception as e: | |
| error_msg = f"❌ Error: {str(e)}" | |
| print(error_msg) | |
| import traceback | |
| traceback.print_exc() | |
| await send_telegram_notification(f"🚨 <b>Lunch Order Check Failed</b>\n\nError: {str(e)}") | |
| raise | |
| # ============================================ | |
| # ENTRY POINT | |
| # ============================================ | |
| if __name__ == "__main__": | |
| print(""" | |
| ╔═══════════════════════════════════════════════════════════╗ | |
| ║ 🍽️ School Lunch Ordering Script ║ | |
| ╠═══════════════════════════════════════════════════════════╣ | |
| ║ 1. Check if meals already ordered ║ | |
| ║ 2. Order only what's missing ║ | |
| ║ 3. Send Telegram notification ║ | |
| ╚═══════════════════════════════════════════════════════════╝ | |
| """) | |
| if not os.getenv("BROWSER_USE_API_KEY"): | |
| print("⚠️ ERROR: BROWSER_USE_API_KEY not set!") | |
| print(" Get free $10 credits at: https://cloud.browser-use.com/new-api-key") | |
| exit(1) | |
| if not CUSTOMER_ID or not PASSWORD: | |
| print("⚠️ ERROR: Credentials not set!") | |
| exit(1) | |
| asyncio.run(order_school_lunch()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # ============================================ | |
| # School Lunch Ordering - Cloud Version | |
| # ============================================ | |
| # | |
| # Uses Browser-Use Cloud - no local browser needed! | |
| # Get free API key: https://cloud.browser-use.com/new-api-key | |
| # | |
| # Usage: ./run_cloud.sh | |
| # ============================================ | |
| # School lunch credentials | |
| export LUNCH_CUSTOMER_ID="" | |
| export LUNCH_PASSWORD="" | |
| # Telegram notification | |
| export TELEGRAM_BOT_TOKEN="" | |
| export TELEGRAM_CHAT_ID="" | |
| # Browser-Use API key (get free $10 at https://cloud.browser-use.com/new-api-key) | |
| # This replaces OpenAI - Browser-Use provides both LLM and browser! | |
| export BROWSER_USE_API_KEY="" | |
| echo "🍽️ Starting School Lunch Ordering (Cloud Version)..." | |
| echo "" | |
| python3 order_lunch.py |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment