Automated monitoring system that tracks ASU course availability and sends real-time notifications via Telegram and email when seats open up.
The system uses AWS serverless infrastructure to continuously monitor course enrollment and deliver notifications through multiple channels.
flowchart TB
subgraph Deployment Pipeline
Dev[Developer]
Build[Docker Build]
ECR[AWS ECR]
end
subgraph AWS Runtime
EB[EventBridge<br/>Scheduled Rule]
Lambda[Lambda Function<br/>Docker Container]
SNS[AWS SNS Topic]
end
subgraph External
ASU[ASU Catalog API]
TG[Telegram Bot API]
Email[Email Subscribers]
end
%% Deployment flow
Dev -->|docker build| Build
Build -->|docker push| ECR
ECR -.->|Image URI| Lambda
%% Runtime flow
EB -->|Triggers on schedule| Lambda
Lambda -->|Queries courses| ASU
ASU -->|Returns enrollment data| Lambda
Lambda -->|Sends alert| TG
Lambda -->|Publishes notification| SNS
SNS -->|Delivers email| Email
- Automated Monitoring - Runs on a configurable schedule via EventBridge, checking for open seats without manual intervention
- Dual Notification Channels - Sends alerts through both Telegram (instant mobile push) and AWS SNS (email delivery)
- Non-Reserved Seat Detection - Filters out reserved seats to report only seats available for general enrollment
- Multi-Course Tracking - Monitors multiple courses simultaneously in a single invocation
- Containerized Deployment - Packaged as Docker images for consistent, reproducible deployments via AWS ECR
- Two Scraping Approaches - Includes both a REST API client (preferred) and a Selenium-based fallback
| Technology | Purpose |
|---|---|
| Python 3.12 | Core application language |
| AWS Lambda | Serverless compute for the monitoring function |
| Docker | Containerized packaging and deployment |
| AWS ECR | Container image registry |
| AWS EventBridge | Scheduled rule to trigger monitoring |
| AWS SNS | Email notification fan-out |
| Telegram Bot API | Real-time mobile push notifications |
| Pandas | Data processing for API response parsing |
| Selenium + Chrome | Headless browser scraping (fallback approach) |
-
Trigger - An EventBridge scheduled rule invokes the Lambda function at a configured interval (e.g., every 5 minutes during enrollment windows).
-
Data Retrieval - The Lambda function queries the ASU catalog API for current enrollment data on the specified courses.
-
Availability Check - For each monitored course, the function compares enrollment capacity against current enrollment and reserved seats to compute available non-reserved seats.
-
Notification - When open seats are detected:
- A Telegram message is sent immediately for real-time mobile alerts
- An SNS message is published, which triggers email delivery to all topic subscribers
-
Response - The function returns a summary of findings, enabling CloudWatch logging for audit trails.
ASU-Course-Notifier/
├── .env.example # Environment variable template
├── .gitignore
├── README.md
├── docs/
│ └── architecture.md # Detailed architecture documentation
└── lambdas/
├── api-based/ # REST API approach (preferred)
│ ├── Dockerfile
│ ├── lambda_function.py
│ └── requirements.txt
└── selenium-scraper/ # Selenium scraping approach (fallback)
├── Dockerfile
├── lambda_function.py
└── requirements.txt
This project includes two implementations for retrieving course data:
Located in lambdas/api-based/. Queries the ASU catalog REST API directly and parses the JSON response using Pandas. This is the recommended approach because it is:
- Faster - Direct API calls without browser overhead
- More Reliable - Not dependent on HTML structure or page rendering
- Lighter - Smaller container image (~200MB vs ~800MB+)
- Easier to Maintain - Structured JSON data instead of DOM parsing
Located in lambdas/selenium-scraper/. Uses headless Chrome via Selenium to render the ASU course catalog website and parses the HTML with BeautifulSoup. This approach exists as a fallback if the REST API becomes unavailable or changes its access patterns.
While both lambdas monitor for open seats and send notifications, they differ in behavior:
- Seat calculation: The API-based lambda computes non-reserved seats (total available minus reserved capacity), giving a more accurate picture of seats you can actually enroll in. The Selenium scraper reports raw available seats as displayed on the page, without subtracting reservations.
- Telegram notifications: The API-based lambda sends a Telegram message for every course with open seats. The Selenium scraper only sends Telegram notifications for courses whose title appears in the
telegram_course_listevent parameter, allowing more targeted alerting. - Return values: The API-based lambda returns an empty list when no seats are found. The Selenium scraper returns
False, so callers need different truthy-check logic depending on which lambda is invoked.
- AWS CLI configured with appropriate permissions
- Docker installed locally
- A Telegram bot token (create one via @BotFather)
- An AWS SNS topic with email subscribers configured
Copy the example environment file and fill in your values:
cp .env.example .envEdit .env with your actual values (see Environment Variables below).
docker build --platform linux/amd64 -t asu-course-notifier:latest lambdas/api-based/aws ecr get-login-password --region $AWS_REGION | \
docker login --username AWS --password-stdin <your-account-id>.dkr.ecr.$AWS_REGION.amazonaws.com
docker tag asu-course-notifier:latest \
<your-account-id>.dkr.ecr.$AWS_REGION.amazonaws.com/<your-ecr-repo>:latest
docker push <your-account-id>.dkr.ecr.$AWS_REGION.amazonaws.com/<your-ecr-repo>:latestaws lambda update-function-code \
--function-name <your-function-name> \
--image-uri <your-account-id>.dkr.ecr.$AWS_REGION.amazonaws.com/<your-ecr-repo>:latestCreate a scheduled rule in EventBridge to trigger the Lambda function at your desired interval:
aws events put-rule \
--name "asu-course-check" \
--schedule-expression "rate(5 minutes)"
aws events put-targets \
--rule "asu-course-check" \
--targets "Id"="1","Arn"="<your-lambda-arn>"| Variable | Description | Example |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Telegram bot API token from BotFather | 123456:ABC-DEF1234... |
TELEGRAM_CHAT_ID |
Target Telegram chat/group ID | -1001234567890 |
SNS_TOPIC_ARN |
ARN of the SNS topic for email notifications | arn:aws:sns:us-east-1:<account-id>:<topic> |
AWS_REGION |
AWS region for SNS client | us-east-1 |
ASU_TERM |
ASU term code to monitor | 2257 (Fall 2025) |
See .env.example for a ready-to-use template.
# Build the container
docker build --platform linux/amd64 -t asu-course-notifier:test lambdas/api-based/
# Run with environment variables
docker run --platform linux/amd64 -p 9000:8080 \
-e TELEGRAM_BOT_TOKEN=$TELEGRAM_BOT_TOKEN \
-e TELEGRAM_CHAT_ID=$TELEGRAM_CHAT_ID \
-e SNS_TOPIC_ARN=$SNS_TOPIC_ARN \
-e AWS_REGION=$AWS_REGION \
-e ASU_TERM=$ASU_TERM \
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
asu-course-notifier:test# Trigger the Lambda handler locally
curl -s "http://localhost:9000/2015-03-31/functions/function/invocations" \
-d '{"course_list": ["64343", "88366"]}'docker build --platform linux/amd64 -t asu-selenium:test lambdas/selenium-scraper/
docker run --platform linux/amd64 -p 9000:8080 \
-e TELEGRAM_BOT_TOKEN=$TELEGRAM_BOT_TOKEN \
-e TELEGRAM_CHAT_ID=$TELEGRAM_CHAT_ID \
-e SNS_TOPIC_ARN=$SNS_TOPIC_ARN \
-e AWS_REGION=$AWS_REGION \
-e ASU_TERM=$ASU_TERM \
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
asu-selenium:test- Web Dashboard - Add a simple web UI to view monitored courses and notification history
- Multi-Subject Support - Extend beyond CSE to monitor any ASU department
- Seat Reservation Tracking - Track reserved vs. unreserved seats over time with historical data
- Waitlist Monitoring - Alert when waitlist positions change or when courses re-open
- Configurable Schedules - Allow per-course monitoring frequency via DynamoDB configuration
- SMS Notifications - Add SMS delivery via SNS for critical alerts
- Cost Optimization - Implement conditional scheduling (pause during non-enrollment periods)
The original class_search.py was derived from remiliacn/ASU-Class-Search.
