|
| 1 | +# Telegram Group Photo Wall |
| 2 | + |
| 3 | +Real-time photo wall that displays messages and images from Telegram groups. Each group gets its own dedicated photo wall page. Messages appear as colorful sticky notes with quantum signatures generated by AWS Braket SV1. |
| 4 | + |
| 5 | +## Architecture |
| 6 | + |
| 7 | +``` |
| 8 | +Telegram Bot → CloudFront (wall.example.com) |
| 9 | + → ALB → ECS Fargate (Next.js) |
| 10 | + ├── DynamoDB (messages + positions) |
| 11 | + ├── S3 (photos + Braket results) |
| 12 | + ├── Secrets Manager (bot tokens, admin password) |
| 13 | + └── AWS Braket SV1 (quantum signatures) |
| 14 | +``` |
| 15 | + |
| 16 | +- **ECS Fargate** running Next.js (standalone Docker) |
| 17 | +- **ALB** with CloudFront secret header validation (blocks direct access) |
| 18 | +- **CloudFront** with custom domain + ACM certificate |
| 19 | +- **Route53** DNS alias record → CloudFront |
| 20 | +- **DynamoDB** for message storage (pay-per-request, encrypted) |
| 21 | +- **S3** for photo storage (private, encrypted, versioned) |
| 22 | +- **AWS Braket SV1** for quantum random number + Bell state measurement |
| 23 | + |
| 24 | +## Features |
| 25 | + |
| 26 | +- Colorful sticky note wall with sci-fi background |
| 27 | +- Real-time 5-second polling for new messages |
| 28 | +- Only messages that **@mention the bot** are shown on the wall |
| 29 | +- Quantum signature per user (first message triggers Braket SV1, subsequent reuse) |
| 30 | +- Draggable cards with position persistence (percentage-based, responsive) |
| 31 | +- Fullscreen layout, responsive to window resize |
| 32 | +- Event logo overlay at top center |
| 33 | +- User leaderboard at bottom (configurable count) |
| 34 | +- Admin dashboard with password login |
| 35 | +- Soft-delete (hide) individual or all messages |
| 36 | +- Custom domain support via Route53 + ACM |
| 37 | + |
| 38 | +## Prerequisites |
| 39 | + |
| 40 | +- Node.js 20+ |
| 41 | +- AWS CDK CLI (`npm install -g aws-cdk`) |
| 42 | +- AWS account with CDK bootstrapped (`cdk bootstrap`) |
| 43 | +- Docker (for building ECS container image) |
| 44 | +- Telegram Bot token(s) created via [@BotFather](https://t.me/BotFather) |
| 45 | + |
| 46 | +## Setup |
| 47 | + |
| 48 | +### 1. Store Bot Tokens in AWS Secrets Manager |
| 49 | + |
| 50 | +```bash |
| 51 | +aws secretsmanager create-secret \ |
| 52 | + --name "telegram/bot-token/demo-group" \ |
| 53 | + --secret-string "YOUR_BOT_TOKEN_HERE" \ |
| 54 | + --region us-west-2 |
| 55 | +``` |
| 56 | + |
| 57 | +### 2. Configure Groups in `cdk.json` |
| 58 | + |
| 59 | +```json |
| 60 | +{ |
| 61 | + "context": { |
| 62 | + "telegramGroups": [ |
| 63 | + { |
| 64 | + "groupId": "my-team", |
| 65 | + "chatId": "-1001234567890", |
| 66 | + "name": "My Team Photo Wall", |
| 67 | + "secretName": "telegram/bot-token/my-team", |
| 68 | + "botUsername": "my_photo_wall_bot" |
| 69 | + } |
| 70 | + ] |
| 71 | + } |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +| Field | Description | Example | |
| 76 | +|-------|-------------|---------| |
| 77 | +| `groupId` | URL identifier for the wall | `my-team` | |
| 78 | +| `chatId` | Telegram group Chat ID | `-1001234567890` | |
| 79 | +| `name` | Display name on the wall | `My Team Photo Wall` | |
| 80 | +| `secretName` | Secrets Manager name for bot token | `telegram/bot-token/my-team` | |
| 81 | +| `botUsername` | Bot's @username (only @mentions shown) | `my_photo_wall_bot` | |
| 82 | + |
| 83 | +### 3. Configure Custom Domain (Optional) |
| 84 | + |
| 85 | +If you want a custom domain like `wall.example.com`: |
| 86 | + |
| 87 | +#### a. Request ACM Certificate (must be in us-east-1) |
| 88 | + |
| 89 | +```bash |
| 90 | +aws acm request-certificate \ |
| 91 | + --domain-name "wall.example.com" \ |
| 92 | + --validation-method DNS \ |
| 93 | + --region us-east-1 |
| 94 | +``` |
| 95 | + |
| 96 | +#### b. Validate via Route53 |
| 97 | + |
| 98 | +```bash |
| 99 | +# Get the DNS validation record from ACM, then add to Route53 |
| 100 | +# See docs/telegram-integration-guide.md for detailed steps |
| 101 | +``` |
| 102 | + |
| 103 | +#### c. Add Domain Config to `cdk.json` |
| 104 | + |
| 105 | +```json |
| 106 | +{ |
| 107 | + "context": { |
| 108 | + "domain": { |
| 109 | + "name": "wall.example.com", |
| 110 | + "hostedZoneId": "ZXXXXXXXXXXXXX", |
| 111 | + "hostedZoneName": "example.com", |
| 112 | + "certificateArn": "arn:aws:acm:us-east-1:123456789:certificate/xxx-xxx" |
| 113 | + }, |
| 114 | + "telegramGroups": [...] |
| 115 | + } |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +| Field | Description | |
| 120 | +|-------|-------------| |
| 121 | +| `domain.name` | Full subdomain (e.g. `wall.example.com`) | |
| 122 | +| `domain.hostedZoneId` | Route53 Hosted Zone ID | |
| 123 | +| `domain.hostedZoneName` | Root domain (e.g. `example.com`) | |
| 124 | +| `domain.certificateArn` | ACM certificate ARN (must be in **us-east-1**) | |
| 125 | + |
| 126 | +CDK will automatically create a Route53 A record alias pointing to CloudFront. |
| 127 | + |
| 128 | +### 4. Deploy |
| 129 | + |
| 130 | +```bash |
| 131 | +./deploy.sh |
| 132 | +``` |
| 133 | + |
| 134 | +Or manually: |
| 135 | + |
| 136 | +```bash |
| 137 | +npm install |
| 138 | +cd photo-wall && npm install && npm run build && cd .. |
| 139 | +npx cdk deploy |
| 140 | +``` |
| 141 | + |
| 142 | +### 5. Set Telegram Webhook |
| 143 | + |
| 144 | +```bash |
| 145 | +WEBHOOK_SECRET=$(aws secretsmanager get-secret-value \ |
| 146 | + --secret-id telegram/webhook-secret \ |
| 147 | + --query SecretString --output text \ |
| 148 | + --region us-west-2) |
| 149 | + |
| 150 | +curl -X POST "https://api.telegram.org/bot<BOT_TOKEN>/setWebhook" \ |
| 151 | + -H "Content-Type: application/json" \ |
| 152 | + -d '{ |
| 153 | + "url": "https://<YOUR_CLOUDFRONT_DOMAIN>/api/webhook/demo-group", |
| 154 | + "secret_token": "'$WEBHOOK_SECRET'", |
| 155 | + "allowed_updates": ["message"] |
| 156 | + }' |
| 157 | +``` |
| 158 | + |
| 159 | +### 6. Add Bot to Group |
| 160 | + |
| 161 | +1. Add your bot to the Telegram group |
| 162 | +2. Turn off **Privacy Mode** in BotFather (Bot Settings → Group Privacy → Turn off) |
| 163 | +3. Users send messages with `@your_bot_username` to have them appear on the wall |
| 164 | + |
| 165 | +## Admin Dashboard |
| 166 | + |
| 167 | +Access: `https://<YOUR_CLOUDFRONT_DOMAIN>/admin` |
| 168 | + |
| 169 | +### Get Admin Password |
| 170 | + |
| 171 | +```bash |
| 172 | +aws secretsmanager get-secret-value \ |
| 173 | + --secret-id "telegram/admin-password" \ |
| 174 | + --query SecretString --output text \ |
| 175 | + --region us-west-2 |
| 176 | +``` |
| 177 | + |
| 178 | +### Change Admin Password |
| 179 | + |
| 180 | +```bash |
| 181 | +aws secretsmanager update-secret \ |
| 182 | + --secret-id "telegram/admin-password" \ |
| 183 | + --secret-string "your-new-password" \ |
| 184 | + --region us-west-2 |
| 185 | +``` |
| 186 | + |
| 187 | +### Admin Features |
| 188 | + |
| 189 | +- **Login** — Password-protected admin page |
| 190 | +- **Message list** — View all messages with sender, text, type, quantum signature status |
| 191 | +- **Hide message** — Soft-delete individual messages (data preserved in DynamoDB) |
| 192 | +- **Clear all** — Soft-delete all messages for a group |
| 193 | +- **Group selector** — Switch between groups (if multiple configured) |
| 194 | + |
| 195 | +## Quantum Signatures |
| 196 | + |
| 197 | +Each user gets a unique quantum signature generated by **AWS Braket SV1** quantum simulator: |
| 198 | + |
| 199 | +1. **First message** from a sender → submits 2 quantum tasks to Braket SV1: |
| 200 | + - 4-qubit random number circuit (H + CNOT + Ry rotations, 100 shots) |
| 201 | + - 2-qubit Bell state circuit (|Φ+⟩, 200 shots) |
| 202 | +2. **Subsequent messages** from the same sender → reuse the existing signature (no Braket call) |
| 203 | +3. Signature displayed as `Q#452 | 7B284BB3D413` on each card |
| 204 | +4. Hover to see full details (quantum number, public key, signature, device, algorithm) |
| 205 | +5. If Braket fails, gracefully falls back to local crypto-based signature |
| 206 | + |
| 207 | +## ECS Health Check |
| 208 | + |
| 209 | +- **Path**: `/api/health` |
| 210 | +- **Interval**: 15s, **Timeout**: 5s |
| 211 | +- **Healthy/Unhealthy threshold**: 2 |
| 212 | + |
| 213 | +## Security |
| 214 | + |
| 215 | +- Bot tokens in Secrets Manager (never in code/env) |
| 216 | +- Admin password in Secrets Manager (auto-generated) |
| 217 | +- Webhook requests validated via Telegram secret token |
| 218 | +- ALB restricted to CloudFront IPs via AWS prefix list |
| 219 | +- CloudFront secret header prevents direct ALB access |
| 220 | +- Custom domain with ACM TLS certificate |
| 221 | +- S3 fully private (no public access) |
| 222 | +- DynamoDB/S3 encrypted at rest |
| 223 | +- HTTPS everywhere (CloudFront → viewer) |
| 224 | +- Input sanitization (XSS prevention) |
| 225 | +- Security headers: HSTS, X-Frame-Options, CSP, XSS protection |
| 226 | +- IAM least-privilege for ECS task role |
| 227 | +- Container runs as non-root user |
| 228 | + |
| 229 | +## Other Docs |
| 230 | + |
| 231 | +- [Telegram Integration Guide](docs/telegram-integration-guide.md) — Detailed Telegram setup |
| 232 | +- [Add New Group](docs/add-new-group.md) — Create a new group photo wall |
| 233 | +- [Local Development](docs/local-development.md) — Local dev setup |
| 234 | + |
| 235 | +## Project Structure |
| 236 | + |
| 237 | +``` |
| 238 | +telegram-photo-wall/ |
| 239 | +├── bin/app.ts # CDK entry |
| 240 | +├── lib/telegram-photo-wall-stack.ts # Infrastructure |
| 241 | +├── photo-wall/ # Next.js application |
| 242 | +│ ├── src/ |
| 243 | +│ │ ├── app/ |
| 244 | +│ │ │ ├── page.tsx # Home (redirect) |
| 245 | +│ │ │ ├── wall/[groupId]/page.tsx # Photo wall page |
| 246 | +│ │ │ ├── admin/page.tsx # Admin dashboard |
| 247 | +│ │ │ └── api/ |
| 248 | +│ │ │ ├── health/route.ts # Health check |
| 249 | +│ │ │ ├── webhook/[groupId]/ # Telegram webhook |
| 250 | +│ │ │ ├── messages/[groupId]/ # Messages CRUD + positions |
| 251 | +│ │ │ ├── groups/route.ts # Groups list |
| 252 | +│ │ │ └── admin/route.ts # Admin login + actions |
| 253 | +│ │ ├── components/ |
| 254 | +│ │ │ ├── PhotoWall.tsx # Main wall with polling + drag |
| 255 | +│ │ │ ├── MessageCard.tsx # Sticky note card + quantum badge |
| 256 | +│ │ │ ├── GroupNav.tsx # Group switcher |
| 257 | +│ │ │ └── Lightbox.tsx # Image preview |
| 258 | +│ │ └── lib/ |
| 259 | +│ │ ├── aws.ts # AWS SDK clients |
| 260 | +│ │ ├── config.ts # Group config |
| 261 | +│ │ ├── sanitize.ts # Input sanitization |
| 262 | +│ │ └── quantum-signature.ts # Braket SV1 quantum signature |
| 263 | +│ ├── public/logo.png # Event banner logo |
| 264 | +│ ├── Dockerfile # Multi-stage Node 20 Alpine |
| 265 | +│ └── package.json |
| 266 | +├── scripts/ |
| 267 | +│ ├── setup-local-env.sh # Generate .env.local from CDK |
| 268 | +│ ├── set-webhook-local.sh # Webhook → ngrok (local dev) |
| 269 | +│ └── set-webhook-prod.sh # Webhook → production |
| 270 | +├── docs/ |
| 271 | +│ ├── requirements.md # Requirements document |
| 272 | +│ ├── telegram-integration-guide.md # Telegram setup guide |
| 273 | +│ ├── add-new-group.md # Add new group guide |
| 274 | +│ └── local-development.md # Local dev guide |
| 275 | +├── deploy.sh # One-click deployment |
| 276 | +├── cdk.json # CDK + group + domain config |
| 277 | +└── package.json # CDK dependencies |
| 278 | +``` |
0 commit comments