# ๐ฆ Dad Bot โ WhatsApp Academic Assistant
> A self-hosted AI bot for your family WhatsApp group that helps your kids with schoolwork, powered by Amazon Bedrock Nova 2 and OpenClaw on AWS EC2.
Built as a weekend project for a NSW family with two students โ one doing HSC and one in Year 8. The bot responds to trigger commands from parents, adapts its tone to each student, and runs on a schedule to keep AWS costs under $25/month.
---
## What It Does
- **Responds in the family WhatsApp group** when mentioned or triggered
- **Parent commands**: `!hsc [topic]`, `!yr8 [topic]`, `!quiz`, `!fact`, `!topic`
- **Student-aware**: different tone and depth for HSC vs Year 8
- **Runs 6:30 AM โ 10:30 PM** via Lambda scheduler โ off overnight
- **Backs up credentials to S3** every 30 minutes โ survives redeployment
---
## Architecture
```
Family WhatsApp โโWebSocketโโโบ OpenClaw Gateway (EC2 t4g.medium)
โ
Amazon Bedrock Nova 2 Lite
(us-east-1, global inference profile)
โ
~/.openclaw/workspace/SOUL.md
(persona, injected every turn)
S3 Bucket โโโ backup.sh (cron every 30 mins)
creds.json + openclaw.json + SOUL.md
Lambda + EventBridge
โโโ Start EC2 @ 6:30 AM AEDT
โโโ Stop EC2 @ 10:30 PM AEDT
```
---
## Cost (~$22/month)
| Resource | Monthly |
|----------|---------|
| EC2 t4g.medium (16 hrs/day) | ~$16.13 |
| EBS gp3 20 GB | ~$1.60 |
| Elastic IP (idle 8 hrs/day) | ~$1.20 |
| S3 backup bucket | ~$0.01 |
| Bedrock Nova 2 Lite | ~$0.01 |
| Lambda + EventBridge | Free tier |
| **Total** | **~$19/month** |
---
## Stack
| Component | Technology |
|-----------|-----------|
| Agent runtime | [OpenClaw](https://docs.openclaw.ai) |
| WhatsApp | Baileys (WhatsApp Web protocol) |
| LLM | Amazon Bedrock Nova 2 Lite |
| Compute | EC2 t4g.medium (Graviton ARM64) |
| Scheduler | Lambda + EventBridge |
| Backup | S3 + bash cron |
| IaC | Terraform |
---
## Prerequisites
- AWS account (IAM, EC2, Lambda, Bedrock, S3 permissions)
- [Terraform >= 1.6](https://developer.hashicorp.com/terraform/install)
- AWS CLI configured (`aws configure`)
- A spare phone number for the bot (prepaid SIM/eSIM โ not VoIP)
- Family phone numbers in E.164 format (`+614XXXXXXXX`)
- Existing EC2 key pair in us-east-1
---
## Project Structure
```
dadbot/
โโโ terraform/
โ โโโ main.tf โ All AWS infrastructure
โ โโโ variables.tf โ Input variables
โ โโโ outputs.tf โ Post-deploy outputs
โ โโโ ec2_userdata.sh โ EC2 bootstrap script
โ โโโ terraform.tfvars.example โ Copy โ terraform.tfvars
โ โโโ .gitignore
โ โโโ lambda/
โ โโโ ec2_scheduler.py โ EC2 start/stop Lambda (auto-zipped by Terraform)
โโโ config/
โ โโโ openclaw.json5 โ OpenClaw config (gitignored when real numbers added)
โโโ skills/
โ โโโ dadbot/
โ โโโ SKILL.md โ Agent trigger definitions
โโโ scripts/
โ โโโ backup.sh โ S3 backup/restore script
โโโ SOUL.md โ Bot persona template โ scp to ~/.openclaw/workspace/
โโโ .gitignore
```
**Files NOT distributed** (contain personal data):
- `terraform/terraform.tfvars` โ your AWS key pair name and home IP
- `config/openclaw.json5` with real phone numbers โ use the example as template
---
## Part 0: Before You Start
### Enable Bedrock Model Access
Nova 2 uses cross-region inference profiles โ no manual approval needed unlike Nova 1.
Verify Nova 2 is available in your account:
```bash
aws bedrock list-inference-profiles --region us-east-1 --type-equals SYSTEM_DEFINED --query 'inferenceProfileSummaries[?contains(inferenceProfileId, `nova-2`)].[inferenceProfileId,status]' --output table
```
You should see `global.amazon.nova-2-lite-v1:0` and `us.amazon.nova-2-lite-v1:0` with status `ACTIVE`.
If Nova 2 is not listed, go to AWS Console โ Bedrock โ Cross-region inference (us-east-1) to confirm availability in your region.
### Get a Spare Phone Number
WhatsApp requires a real mobile number (not VoIP) for verification. After the initial SMS verification, the SIM can sit in a drawer โ the session persists on EC2 via `creds.json`.
**Australia options (~$10 once-off):**
- Woolworths Mobile โ $10 starter, annual recharge to keep alive
- Aldi Mobile โ $15 starter
- Boost Mobile โ $10 starter
---
## Part 1: Deploy Infrastructure
```bash
cd terraform
cp terraform.tfvars.example terraform.tfvars
```
Edit `terraform.tfvars`:
```hcl
aws_region = "us-east-1"
instance_type = "t4g.medium" # Graviton ARM โ 20% cheaper than x86
# Name of your existing EC2 key pair (not the key ID)
# aws ec2 describe-key-pairs --region us-east-1 --output table
key_pair_name = "your-key-name"
# Your home IP: https://whatismyip.com
admin_ip_cidr = "x.x.x.x/32"
alert_email = "you@example.com" # optional SNS alerts
```
```bash
terraform init
terraform plan # review ~22 resources
terraform apply # ~3 minutes
```
Note the outputs โ you'll need `elastic_ip` and `backup_bucket`.
---
## Part 2: Configure OpenClaw
### Edit the Config
Fill in your family's phone numbers in `config/openclaw.json5`:
```json5
allowFrom: [
"+61XXXXXXXXXX", // Mum
"+61XXXXXXXXXX", // Dad
"+61XXXXXXXXXX", // Student 1
"+61XXXXXXXXXX", // Student 2
],
```
> The bot's own spare SIM number does **not** go in `allowFrom`.
### Copy Files to EC2
```bash
# Wait ~5 mins for EC2 bootstrap first
ssh -i ~/.ssh/id_rsa ubuntu@<ELASTIC_IP> "tail -f /var/log/dadbot-setup.log"
# Copy project files
scp -r ./config ./skills ./scripts ubuntu@<ELASTIC_IP>:~/dadbot/
# Copy SOUL.md to the OpenClaw workspace directory
# This is the bot persona โ injected into every conversation turn
scp ./SOUL.md ubuntu@<ELASTIC_IP>:~/.openclaw/workspace/SOUL.md
```
> Edit `SOUL.md` to personalise for your family before copying โ update student names, subjects, and year levels. The file on EC2 at `~/.openclaw/workspace/SOUL.md` is injected into every conversation turn. To update it live: `nano ~/.openclaw/workspace/SOUL.md` then `sudo systemctl restart openclaw`. It is also backed up to S3 by `backup.sh` every 30 minutes.
### Onboard OpenClaw
```bash
ssh -i ~/.ssh/id_rsa ubuntu@<ELASTIC_IP>
openclaw onboard --non-interactive --auth-choice bedrock --accept-risk
cp ~/dadbot/config/openclaw.json5 ~/.openclaw/openclaw.json
```
---
## Part 3: Connect WhatsApp
```bash
sudo systemctl start openclaw
sudo systemctl status openclaw # confirm running
openclaw channels login --channel whatsapp
```
On the **bot phone**:
1. WhatsApp โ Settings โ Linked Devices โ Link a Device
2. Scan the QR code
> If QR renders garbled over SSH: `TERM=xterm-256color openclaw channels login --channel whatsapp`
### Enable Autostart
```bash
sudo systemctl enable openclaw
```
---
## Part 4: Set Up Backups
```bash
# Test backup manually first
export DADBOT_BACKUP_BUCKET=$(cat ~/dadbot/config/backup-bucket)
~/dadbot/scripts/backup.sh backup
# Verify files in S3
aws s3 ls s3://$DADBOT_BACKUP_BUCKET/dadbot-backup/latest/
# Set up cron (every 30 minutes)
crontab -e
```
Add this line:
```
*/30 * * * * /home/ubuntu/dadbot/scripts/backup.sh >> /var/log/dadbot-backup.log 2>&1
```
---
## Part 5: Test the Bot
Watch logs live:
```bash
journalctl -u openclaw -f
```
**Send these from your WhatsApp in the family group:**
| Command | What it does |
|---------|-------------|
| `dadbot hello` | Basic test |
| `!fact` | Random curriculum fact |
| `!hsc chemistry equilibrium` | HSC-focused mini lesson |
| `!yr8 volcanoes` | Year 8 level explainer |
| `!quiz` | Random quiz question |
| `!quiz [name] maths` | Targeted quiz |
| `dadbot explain photosynthesis` | Direct question |
**Test guardrails:**
```
dadbot write my essay for me
dadbot what's on Netflix
```
Should redirect: *"That's outside my lane โ ask me something you're studying!"*
---
## Part 6: Scheduler
The Lambda scheduler is already deployed. Test it manually:
```bash
# Stop EC2
aws lambda invoke --function-name dadbot-ec2-scheduler \
--payload '{"action":"stop"}' --cli-binary-format raw-in-base64-out \
/tmp/r.json --region us-east-1 && cat /tmp/r.json
# Start EC2
aws lambda invoke --function-name dadbot-ec2-scheduler \
--payload '{"action":"start"}' --cli-binary-format raw-in-base64-out \
/tmp/r.json --region us-east-1 && cat /tmp/r.json
```
### Daylight Saving
Update `terraform.tfvars` and re-apply:
| Season | Start | Stop |
|--------|-------|------|
| AEDT (OctโApr, UTC+11) | `cron(30 19 * * ? *)` | `cron(30 11 * * ? *)` |
| AEST (AprโOct, UTC+10) | `cron(30 20 * * ? *)` | `cron(30 12 * * ? *)` |
---
## Customising the Bot
### Update the Persona
```bash
nano ~/.openclaw/workspace/SOUL.md
sudo systemctl restart openclaw
```
### Add Trigger Commands
Add to `mentionPatterns` in `openclaw.json5`, describe in `skills/dadbot/SKILL.md`.
### Browser UI (testing without WhatsApp)
```bash
# SSH tunnel from local machine
ssh -i ~/.ssh/id_rsa -L 18791:127.0.0.1:18791 ubuntu@<ELASTIC_IP>
```
Open `http://localhost:18791`
### View Memory
```bash
sqlite3 ~/.openclaw/openclaw.db "SELECT * FROM memories LIMIT 20;"
```
---
## Troubleshooting
| Problem | Fix |
|---------|-----|
| Bot not responding | `journalctl -u openclaw -f` |
| WhatsApp QR expired | `openclaw channels login --channel whatsapp` |
| Gateway crash loop | `sudo systemctl status openclaw` โ check logs |
| Bedrock auth error | Verify EC2 IAM role has Bedrock policy; `AWS_PROFILE=default` in service |
| 91k token spike | Check `~/.openclaw/openclaw.db` session size; restart to clear |
| EC2 not starting on schedule | CloudWatch โ `/aws/lambda/dadbot-ec2-scheduler` |
| SSH blocked after IP change | Update `admin_ip_cidr` in `terraform.tfvars` โ `terraform apply` |
| Restore from backup | `~/dadbot/scripts/backup.sh restore` |
---
## Tear Down
```bash
cd terraform
terraform destroy
```
---
## References
- [OpenClaw docs](https://docs.openclaw.ai)
- [OpenClaw WhatsApp](https://docs.openclaw.ai/channels/whatsapp)
- [OpenClaw Bedrock provider](https://docs.openclaw.ai/providers/bedrock)
- [OpenClaw system prompt / SOUL.md](https://docs.openclaw.ai/concepts/system-prompt)
- [Amazon Nova 2 models](https://aws.amazon.com/bedrock/nova/)
- [AWS Bedrock pricing](https://aws.amazon.com/bedrock/pricing/)
- [AWS EC2 pricing](https://aws.amazon.com/ec2/pricing/on-demand/)
- [NSW HSC syllabi](https://educationstandards.nsw.edu.au/wps/portal/nesa/11-12)
- [NSW Stage 4 curriculum](https://educationstandards.nsw.edu.au/wps/portal/nesa/k-10)