Hydration Station
A serverless Discord bot that nudges a server to drink water and lets members log their intake with slash commands. It runs fully serverless on AWS (Lambda, DynamoDB, and EventBridge), so it stays online around the clock for effectively $0 a month.
Fully deployed and running within the AWS always-free tier at roughly $0/month, supporting multi-user servers.
Project Overview
Reminds a channel to hydrate on a configurable schedule and tracks each person's intake against their own daily goals.
Staying hydrated is easy to forget, and existing reminder bots either need an always-on server (a recurring cost) or are one-size-fits-all.
A self-hostable, fully serverless bot that costs nothing to keep online and needs no server to manage.
Core Features
Flexible Intake Logging
- Log in cups, bottles (custom size), ml, or oz
- Quick correction with /undo
- Timezone-aware day boundaries
Personal Daily Goals
- Per-person daily targets
- Per-weekday goals (for example, higher on workout days)
- Progress tracked against each goal
Configurable Reminders
- Every N hours within a set window
- Or fixed times of day
- Timezone-aware scheduling
Slash Commands
- /today and /progress
- /history and /leaderboard
- /undo
Individual or Team
- Per-person tracking
- Shared team-goal mode
- Admin-gated switch between modes
Smart Reminders
- @mentions only people behind their goal
- Shows each person's progress
- Skips anyone already done
How It Works
Interactions Lambda
Discord calls a public Lambda Function URL on every slash command. The function verifies the request signature, reads and writes DynamoDB, and replies within Discord's roughly 3-second window.
Reminder Lambda
A single EventBridge schedule fires every 15 minutes. The function checks each server's configured schedule and posts a reminder only when one is actually due.
Single-Table DynamoDB
One table holds server config, per-user profiles and goals, atomic daily rollup counters, and individual log entries.
No API Gateway
A public Lambda Function URL serves the endpoint directly, keeping the stack to the fewest possible moving parts.
Tools + Build
Decisions Worth Calling Out
Serverless for $0
An always-on gateway bot needs a process running 24/7, which recurs as a cost. Modeling the bot as HTTP interactions plus a scheduled function keeps every piece inside AWS's always-free tier, so it runs indefinitely for free.
Zero Third-Party Dependencies
Everything uses the Python standard library plus AWS's built-in boto3. No dependency packaging, no binary-wheel mismatches, and the code pastes straight into the Lambda console. That even meant hand-writing a pure-Python Ed25519 verifier instead of pulling in PyNaCl.
Atomic Counters
Daily totals use atomic increment updates, avoiding read-modify-write races. Every logged drink updates both a per-user rollup and a server-wide rollup, so the bot can switch between individual and shared team modes without ever losing history.
One Static Schedule
Instead of creating and deleting schedules when users change settings, a single 15-minute trigger runs the Lambda, which decides in code what's due (interval window or fixed times). Simpler, cheaper, and stateless.
Security by Verification
The Function URL is public, but every request is cryptographically verified against the app's Ed25519 public key before processing (the standard Discord model), with least-privilege IAM scoping each Lambda to exactly the actions it needs.
Thoughtful UX
Drink logs post publicly as a social nudge, while private replies auto-delete after about 25 seconds (via an async self-invoke, since Discord has no native message TTL). Reminders mention only the people who are behind, and show their progress.
Notable Challenge
A Mysterious Timeout
The Discord endpoint kept failing verification with a vague error. Logs showed the Lambda hitting a 10-second timeout. The first pass of the pure-Python Ed25519 verifier used affine coordinates, doing a modular inverse on every step. Fine on a laptop, but on a 128 MB Lambda (a sliver of a vCPU) a single signature took over 10 seconds.
A ~1000x Speedup
Rewriting the verifier around the RFC 8032 extended-coordinate algorithm removed the per-step inversion and brought verification down to about 5 milliseconds, validated against the official RFC 8032 test vectors. A clean reminder that algorithmic complexity matters most where CPU is scarce.
Practices Worth Highlighting
Tested Without the Cloud
An in-memory boto3 and DynamoDB stub runs the real handlers end to end offline, and the crypto is validated against RFC 8032 vectors, so the whole thing is testable with no cloud access.
Least-Privilege IAM
Each function is scoped to its specific DynamoDB table actions, plus a single self-invoke permission for the interactions function and nothing more.
Graceful Degradation
Command dispatch always returns a reply, and optional features like auto-delete fail silently rather than breaking the command.