cms.c2sgmbh/prompts/PROMPT_TELEGRAM_MEDIA_BOT.md
Martin Porwoll 52a266d72d docs: add telegram media bot plan and sensualmoment design docs
- Telegram media bot implementation plan and prompt
- sensualmoment.de design prototypes (color scheme, prototype, design doc)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:14:44 +00:00

749 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Telegram Media Upload Bot Payload CMS Integration
## Kontext
- **Projektverzeichnis:** `/home/payload/telegram-media-bot` (auf sv-payload, LXC 700, IP: 10.10.181.100)
- **Alternative:** Neues GitHub Repository `complexcaresolutions/telegram-media-bot`
- **Tech-Stack:** Node.js 22 LTS, TypeScript, grammy (Telegram Bot Framework), PM2
- **Ziel-System:** Payload CMS Multi-Tenant (Production: `https://cms.c2sgmbh.de`, Staging: `https://pl.porwoll.tech`)
- **Betriebssystem:** Debian/Ubuntu (LXC Container im Proxmox-Cluster)
- **Package Manager:** pnpm
---
## Projektbeschreibung
Erstelle einen Telegram Bot, der es autorisierten Benutzern ermöglicht, Bilder direkt aus dem Telegram-Chat in die Media Collection des Payload CMS hochzuladen. Der Bot authentifiziert sich gegen die Payload REST-API, empfängt Bilder über die Telegram Bot API, lädt sie von den Telegram-Servern herunter und leitet sie per `multipart/form-data` an den Payload Media-Endpoint weiter. Dabei wird die Multi-Tenant-Isolation strikt eingehalten.
---
## Technische Referenzen
### Payload CMS REST-API
**Base URLs:**
- Production: `https://cms.c2sgmbh.de/api`
- Staging: `https://pl.porwoll.tech/api`
- Swagger UI: `https://cms.c2sgmbh.de/api/docs`
**Authentifizierung Login:**
```bash
curl -X POST "https://cms.c2sgmbh.de/api/users/login" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"password": "your-password"
}'
```
**Response:**
```json
{
"message": "Auth Passed",
"user": { "id": 1, "email": "admin@example.com", "isSuperAdmin": true },
"token": "eyJhbGciOiJIUzI1NiIs..."
}
```
**Token verwenden:**
```bash
curl "https://cms.c2sgmbh.de/api/media" \
-H "Authorization: JWT eyJhbGciOiJIUzI1NiIs..."
```
### Media Upload Endpoint
```bash
curl -X POST "https://cms.c2sgmbh.de/api/media" \
-H "Authorization: JWT <token>" \
-F "file=@/path/to/image.jpg" \
-F "alt=Beschreibungstext" \
-F "tenant=1"
```
**WICHTIG:** Das `tenant`-Feld ist **Pflicht** bei jedem Upload. Ohne korrekte Tenant-Zuordnung gibt die API 403 Forbidden zurück. Die Tenant-Isolation ist ein Kernprinzip des gesamten Systems.
### Verfügbare Tenants
| ID | Name | Slug | Domain |
|----|------|------|--------|
| 1 | porwoll.de | porwoll | porwoll.de |
| 4 | Complex Care Solutions GmbH | c2s | complexcaresolutions.de |
| 5 | Gunshin | gunshin | gunshin.de |
| (weitere Tenants ggf. dynamisch über API abrufen) |
### Media Collection Automatische Bildverarbeitung
Payload generiert automatisch folgende responsive Größen beim Upload:
| Size | Auflösung | Format |
|------|-----------|--------|
| thumbnail | 150×150 | Original + AVIF |
| small | 300×300 | Original + AVIF |
| medium | 600×600 | Original + AVIF |
| large | 1200×1200 | Original + AVIF |
| xlarge | 1920×1920 | Original + AVIF |
| 2k | 2560×2560 | Original + AVIF |
| og | 1200×630 | Original (Social Media) |
→ Der Bot muss sich NICHT um Bildgrößen kümmern. Payload erledigt das serverseitig.
### Rate Limiting (Payload API)
| Limiter | Limit | Fenster |
|---------|-------|---------|
| publicApiLimiter | 60 Requests | 1 Minute |
| authLimiter | 5 Requests | 15 Minuten |
→ Der Bot sollte Token cachen und nicht bei jedem Upload neu einloggen.
---
## Aufgaben
### 1. Projekt-Setup
#### 1.1 Projektstruktur erstellen
```
telegram-media-bot/
├── src/
│ ├── index.ts # Entry Point, Bot-Start
│ ├── bot.ts # Grammy Bot-Instanz + Handler
│ ├── config.ts # Environment-Konfiguration (typisiert)
│ ├── payload/
│ │ ├── client.ts # Payload API Client (Login, Token-Management)
│ │ └── media.ts # Media Upload Logik
│ ├── telegram/
│ │ ├── handlers.ts # Message Handler (Photo, Document, Commands)
│ │ └── keyboards.ts # Inline Keyboards (Tenant-Auswahl etc.)
│ ├── middleware/
│ │ └── auth.ts # User-Whitelist Middleware
│ └── utils/
│ ├── logger.ts # Logging Utility
│ └── download.ts # Telegram File Download Helper
├── .env.example # Template für Environment Variables
├── .env # (gitignored) Actual Config
├── package.json
├── tsconfig.json
├── ecosystem.config.cjs # PM2 Konfiguration
├── .gitignore
└── README.md
```
**Akzeptanzkriterien:**
- [ ] `pnpm init` ausgeführt
- [ ] TypeScript konfiguriert (strict mode)
- [ ] Alle Dependencies installiert
- [ ] `.gitignore` enthält `node_modules`, `.env`, `dist`
#### 1.2 Dependencies installieren
```bash
pnpm add grammy dotenv
pnpm add -D typescript @types/node tsx
```
**Hinweis:** `grammy` ist das bevorzugte Telegram Bot Framework (modern, TypeScript-first, aktiv maintained). Alternativ wäre `node-telegram-bot-api` möglich, aber `grammy` hat bessere TypeScript-Unterstützung.
#### 1.3 TypeScript Konfiguration
**Datei:** `tsconfig.json`
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
#### 1.4 Package.json Scripts
```json
{
"name": "telegram-media-bot",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"lint": "tsc --noEmit"
}
}
```
---
### 2. Konfiguration
#### 2.1 Environment Variables
**Datei:** `.env.example`
```env
# Telegram Bot
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
# Zugelassene Telegram User-IDs (kommasepariert)
ALLOWED_USER_IDS=123456789,987654321
# Payload CMS
PAYLOAD_API_URL=https://cms.c2sgmbh.de/api
PAYLOAD_ADMIN_EMAIL=admin@example.com
PAYLOAD_ADMIN_PASSWORD=your-secure-password
# Standard-Tenant (wird verwendet wenn kein Tenant gewählt)
DEFAULT_TENANT_ID=1
# Logging
LOG_LEVEL=info
# Node Environment
NODE_ENV=production
```
#### 2.2 Typisierte Config
**Datei:** `src/config.ts`
```typescript
// Alle Environment Variables typisiert laden und validieren.
// Bei fehlenden Pflicht-Variablen: Prozess mit Error beenden.
// ALLOWED_USER_IDS als number[] parsen (kommasepariert).
// Validation beim Import, nicht lazy.
interface Config {
telegram: {
botToken: string;
allowedUserIds: number[];
};
payload: {
apiUrl: string;
email: string;
password: string;
};
defaultTenantId: number;
logLevel: string;
nodeEnv: string;
}
```
**Akzeptanzkriterien:**
- [ ] Config wird beim Start validiert
- [ ] Fehlende Pflicht-Variablen = sofortiger Exit mit klarer Fehlermeldung
- [ ] `ALLOWED_USER_IDS` wird als `number[]` geparst
---
### 3. Payload API Client
#### 3.1 Authentifizierung mit Token-Caching
**Datei:** `src/payload/client.ts`
Implementiere einen Payload API Client mit folgender Logik:
1. **Login:** `POST /api/users/login` mit Email/Password
2. **Token speichern** (In-Memory, NICHT auf Disk)
3. **Token-Expiry tracken:** JWT decodieren (ohne Verifikation, nur Payload lesen), `exp` Feld prüfen
4. **Auto-Refresh:** Vor jedem API-Call prüfen ob Token noch gültig (mit 5 Min. Buffer). Falls abgelaufen → neu einloggen.
5. **Retry-Logik:** Bei 401-Response einmal neu einloggen und Request wiederholen.
```typescript
class PayloadClient {
private token: string | null = null;
private tokenExpiry: number = 0;
async getToken(): Promise<string> { /* ... */ }
async login(): Promise<void> { /* ... */ }
async uploadMedia(file: Buffer, filename: string, options: MediaUploadOptions): Promise<MediaResponse> { /* ... */ }
async listMedia(tenantId: number, limit?: number): Promise<MediaListResponse> { /* ... */ }
async deleteMedia(mediaId: number): Promise<void> { /* ... */ }
}
interface MediaUploadOptions {
alt: string;
tenantId: number;
caption?: string;
}
interface MediaResponse {
id: number;
url: string;
filename: string;
alt: string;
sizes: Record<string, { url: string; width: number; height: number }>;
}
```
**WICHTIG Multi-Tenant:**
- Jeder API-Call der Media betrifft MUSS `tenant` als Feld mitsenden
- Bei Reads: `?where[tenant][equals]=<ID>` als Query-Parameter
- Bei Writes: `tenant` im Request-Body
**Akzeptanzkriterien:**
- [ ] Login funktioniert, Token wird gecacht
- [ ] Token wird vor Ablauf automatisch erneuert
- [ ] 401 Response triggert Re-Login + Retry
- [ ] Alle API-Calls enthalten korrekte Tenant-Filterung
#### 3.2 Media Upload Funktion
**Datei:** `src/payload/media.ts`
```typescript
// Upload einer Bilddatei an POST /api/media
// Content-Type: multipart/form-data
//
// Felder:
// file: Die Bilddatei (Buffer) mit korrektem filename + mimetype
// alt: Alt-Text (string)
// tenant: Tenant-ID (number)
//
// Die Payload API generiert automatisch alle responsiven Größen.
// Response enthält die vollständige Media-Resource inkl. aller Size-URLs.
```
Für den multipart Upload verwende die native `FormData` API (ab Node.js 18+ verfügbar) oder `form-data` Package. **Kein axios nötig** nutze native `fetch`.
**Akzeptanzkriterien:**
- [ ] Upload funktioniert mit JPG, PNG, WebP, AVIF
- [ ] Alt-Text wird korrekt gesetzt
- [ ] Tenant-Zuordnung funktioniert
- [ ] Response wird korrekt geparst
---
### 4. Telegram Bot
#### 4.1 Bot-Instanz und Middleware
**Datei:** `src/bot.ts`
```typescript
import { Bot, Context, session } from 'grammy';
interface SessionData {
selectedTenantId: number;
selectedTenantName: string;
}
type BotContext = Context & { session: SessionData };
// Bot erstellen mit Grammy
// Session-Middleware für Tenant-Auswahl pro User
// Auth-Middleware für User-Whitelist
```
#### 4.2 Auth Middleware (User-Whitelist)
**Datei:** `src/middleware/auth.ts`
```typescript
// Middleware die prüft ob ctx.from.id in ALLOWED_USER_IDS enthalten ist.
// Falls nicht: Antwort "⛔ Du bist nicht autorisiert, diesen Bot zu verwenden."
// und ctx.next() NICHT aufrufen.
//
// WICHTIG: Auch in Gruppen-Chats nur auf autorisierte User reagieren.
```
**Akzeptanzkriterien:**
- [ ] Nicht-autorisierte User erhalten Fehlermeldung
- [ ] Autorisierte User können alle Funktionen nutzen
- [ ] Middleware blockiert alle Handler, nicht nur einzelne
#### 4.3 Command Handler
**Datei:** `src/telegram/handlers.ts`
Implementiere folgende Befehle:
**`/start`**
- Begrüßungsnachricht mit Kurzanleitung
- Zeige aktuell gewählten Tenant
- Text:
```
🤖 Payload Media Upload Bot
Schicke mir ein Bild und ich lade es in die Payload CMS Media-Bibliothek hoch.
📌 Aktueller Tenant: [Tenant-Name]
📋 Befehle:
/tenant - Tenant wechseln
/list - Letzte 5 Uploads anzeigen
/status - Bot- und API-Status
/help - Hilfe anzeigen
```
**`/tenant`**
- Zeige Inline-Keyboard mit allen verfügbaren Tenants
- Tenants dynamisch von der API laden: `GET /api/tenants` (Auth erforderlich)
- Nach Auswahl: Tenant in Session speichern
- Bestätigung: `✅ Tenant gewechselt zu: [Name] (ID: [ID])`
**`/list`**
- Zeige die letzten 5 hochgeladenen Medien des aktuellen Tenants
- API: `GET /api/media?where[tenant][equals]=<ID>&sort=-createdAt&limit=5`
- Ausgabe als Liste mit Thumbnail-URL, Dateiname, Datum
**`/status`**
- Zeige:
- Bot-Uptime
- Payload API erreichbar? (Quick-Check: `GET /api/users/me`)
- Aktueller Tenant
- Token-Status (gültig bis...)
**`/help`**
- Ausführliche Hilfe mit allen Befehlen und Nutzungshinweisen
#### 4.4 Photo Handler (Kern-Funktionalität)
**Datei:** `src/telegram/handlers.ts` (fortgesetzt)
```typescript
// Handler für bot.on('message:photo')
//
// Ablauf:
// 1. Höchste verfügbare Auflösung wählen:
// ctx.message.photo ist ein Array von PhotoSize-Objekten,
// sortiert nach Größe. Letztes Element = höchste Auflösung.
//
// 2. File-Info abrufen:
// const file = await ctx.api.getFile(photo.file_id)
// Download-URL: https://api.telegram.org/file/bot<TOKEN>/<file.file_path>
//
// 3. Bild herunterladen (als Buffer):
// fetch() auf die Download-URL
//
// 4. Alt-Text bestimmen:
// - Falls Caption vorhanden (ctx.message.caption) → als Alt-Text verwenden
// - Falls nicht → Generiere: "Upload via Telegram [Datum] [Uhrzeit]"
//
// 5. Statusmeldung senden:
// "⏳ Bild wird hochgeladen..."
//
// 6. An Payload API hochladen:
// payloadClient.uploadMedia(buffer, filename, { alt, tenantId })
//
// 7. Erfolgsmeldung:
// "✅ Upload erfolgreich!
// 📎 ID: [id]
// 📁 Dateiname: [filename]
// 🔗 URL: [url]
// 🏷️ Tenant: [tenant-name]
// 📐 Größen: thumbnail, small, medium, large, xlarge, 2k, og"
//
// 8. Bei Fehler:
// "❌ Upload fehlgeschlagen: [Fehlermeldung]"
// Logge den vollständigen Error serverseitig.
```
**Akzeptanzkriterien:**
- [ ] Bilder werden in höchster Auflösung heruntergeladen
- [ ] Caption wird als Alt-Text verwendet (falls vorhanden)
- [ ] Statusmeldung wird gesendet BEVOR der Upload startet
- [ ] Erfolgsmeldung enthält Media-ID und URL
- [ ] Fehler werden sauber abgefangen und dem User angezeigt
- [ ] Tenant-Zuordnung ist korrekt
#### 4.5 Document Handler (Erweitert)
```typescript
// Handler für bot.on('message:document')
//
// Akzeptiere nur Bildformate: jpg, jpeg, png, webp, avif, gif, svg
// Bei nicht unterstütztem Format:
// "⚠️ Format nicht unterstützt. Erlaubt: JPG, PNG, WebP, AVIF, GIF, SVG"
//
// Vorteil von Document-Upload gegenüber Photo:
// Telegram komprimiert Bilder die als Foto gesendet werden.
// Als Dokument gesendet bleibt die Originalqualität erhalten.
// → Dem User diesen Tipp in /help erklären.
```
#### 4.6 Album/Bulk Handler
```typescript
// Handler für mehrere Bilder gleichzeitig (Media Group / Album)
//
// Telegram sendet Alben als einzelne Messages mit gleicher media_group_id.
// Sammle alle Messages mit gleicher media_group_id über ein kurzes Zeitfenster
// (500ms Debounce), dann lade alle Bilder sequentiell hoch.
//
// Status: "⏳ Album erkannt: [N] Bilder werden hochgeladen..."
// Pro Bild: Fortschritt melden: "📤 [X]/[N] hochgeladen..."
// Am Ende: Zusammenfassung aller Upload-IDs
```
#### 4.7 Inline Keyboards
**Datei:** `src/telegram/keyboards.ts`
```typescript
// Tenant-Auswahl Keyboard
// Dynamisch aus API geladen (GET /api/tenants)
// Format: 2 Buttons pro Reihe
// Jeder Button: callback_data = "tenant:<ID>"
//
// Beispiel:
// [ [porwoll.de] [C2S] ]
// [ [Gunshin] [BlogWoman] ]
// Callback Query Handler:
// Bei "tenant:<ID>" → Session updaten, Bestätigung senden
```
---
### 5. Utilities
#### 5.1 Logger
**Datei:** `src/utils/logger.ts`
```typescript
// Einfacher Logger mit Levels: debug, info, warn, error
// Format: [TIMESTAMP] [LEVEL] [MODULE] Message
// Beispiel: [2026-03-01 14:30:00] [INFO] [PayloadClient] Login erfolgreich
//
// Kein externes Logging-Framework nötig console.log basiert reicht.
// LOG_LEVEL aus config bestimmt Mindest-Level.
```
#### 5.2 Download Helper
**Datei:** `src/utils/download.ts`
```typescript
// Funktion zum Herunterladen einer Datei von einer URL als Buffer.
// Nutze native fetch().
// Timeout: 30 Sekunden
// Max. Dateigröße: 20 MB (Telegram-Limit)
// Bei Fehler: Spezifische Error-Messages (Timeout, Too Large, Network Error)
async function downloadFile(url: string): Promise<{ buffer: Buffer; mimeType: string }> { /* ... */ }
```
---
### 6. PM2 Konfiguration & Deployment
#### 6.1 PM2 Ecosystem File
**Datei:** `ecosystem.config.cjs`
```javascript
module.exports = {
apps: [{
name: 'telegram-media-bot',
script: './dist/index.js',
instances: 1, // NUR 1 Instanz! Telegram Long-Polling verträgt kein Clustering
autorestart: true,
watch: false,
max_memory_restart: '256M',
env: {
NODE_ENV: 'production'
},
error_file: './logs/error.log',
out_file: './logs/out.log',
merge_logs: true,
time: true
}]
};
```
**WICHTIG:** Der Bot nutzt Long-Polling (kein Webhook). Deshalb darf nur EINE Instanz laufen. Mehrere Instanzen führen zu Konflikten bei der Telegram API.
#### 6.2 Deployment auf sv-payload
Der Bot läuft als zusätzlicher PM2-Prozess auf **sv-payload (LXC 700, 10.10.181.100)**, wo bereits das Payload CMS per PM2 managed wird.
```bash
# Auf sv-payload (als user 'payload')
cd /home/payload
git clone git@github.com:complexcaresolutions/telegram-media-bot.git
cd telegram-media-bot
pnpm install
cp .env.example .env
# → .env mit echten Werten befüllen
# Build und Start
pnpm build
pm2 start ecosystem.config.cjs
pm2 save
```
#### 6.3 GitHub Actions Workflow (Optional)
**Datei:** `.github/workflows/deploy.yml`
```yaml
name: Deploy Telegram Bot
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: 37.24.237.181 # Externe IP (UDM Pro SE)
username: payload
key: ${{ secrets.STAGING_SSH_KEY }}
port: 22122 # SSH-Port über Port-Forwarding
script: |
cd /home/payload/telegram-media-bot
git pull origin main
pnpm install --frozen-lockfile
pnpm build
pm2 restart telegram-media-bot
```
**SSH-Zugang:** Externer Zugang über UDM Pro SE Port-Forwarding (Port 22122 → sv-payload:22). Der SSH-Key `STAGING_SSH_KEY` ist bereits als GitHub Secret konfiguriert.
---
### 7. Sicherheit
#### 7.1 Maßnahmen
1. **User-Whitelist:** Nur explizit erlaubte Telegram User-IDs dürfen den Bot nutzen
2. **Token-Sicherheit:** Payload JWT wird nur im Memory gehalten, nie auf Disk geschrieben
3. **Environment Variables:** Alle Secrets in `.env`, nie im Code
4. **Rate Limiting:** Maximal 10 Uploads pro Minute pro User (Bot-seitig implementiert)
5. **Dateigrößen-Limit:** Telegram begrenzt auf 20 MB, zusätzlich Bot-seitiges Limit von 20 MB
6. **Dateityp-Validierung:** Nur erlaubte MIME-Types akzeptieren (image/jpeg, image/png, image/webp, image/avif, image/gif, image/svg+xml)
7. **Kein Webhook-Modus:** Long-Polling vermeidet die Notwendigkeit eines öffentlich erreichbaren Endpoints
#### 7.2 Telegram Bot erstellen
1. Öffne Telegram und suche `@BotFather`
2. Sende `/newbot`
3. Name: `CCS Media Upload Bot` (oder ähnlich)
4. Username: `ccs_media_upload_bot` (muss eindeutig sein und auf `bot` enden)
5. Token sichern → in `.env` als `TELEGRAM_BOT_TOKEN` eintragen
6. Optional via BotFather:
- `/setdescription` Bot-Beschreibung setzen
- `/setcommands` Bot-Befehle registrieren:
```
start - Bot starten und Hilfe anzeigen
tenant - Ziel-Tenant wechseln
list - Letzte Uploads anzeigen
status - Bot- und API-Status prüfen
help - Ausführliche Hilfe
```
---
### 8. Error Handling & Edge Cases
#### 8.1 Zu behandelnde Szenarien
| Szenario | Verhalten |
|----------|-----------|
| Payload API nicht erreichbar | User informieren, Retry nach 30s, Log Error |
| JWT abgelaufen während Upload | Auto-Relogin + Retry (max. 1x) |
| Telegram File Download fehlschlägt | User informieren mit spezifischem Error |
| Bild zu groß (>20 MB) | `⚠️ Datei zu groß. Maximum: 20 MB` |
| Nicht unterstütztes Format | `⚠️ Format nicht unterstützt. Erlaubt: JPG, PNG, WebP, AVIF, GIF, SVG` |
| Kein Tenant gewählt | Default-Tenant verwenden, User informieren |
| Payload 403 (Tenant-Problem) | `❌ Zugriff verweigert. Prüfe die Tenant-Zuordnung.` |
| Payload 429 (Rate Limit) | `⏳ Zu viele Anfragen. Bitte warte [X] Sekunden.` |
| Bot-Start ohne gültige Config | Sofortiger Exit mit klarem Fehlertext |
| Album mit >10 Bildern | Hinweis dass max. 10 gleichzeitig verarbeitet werden |
#### 8.2 Graceful Shutdown
```typescript
// Bei SIGINT/SIGTERM:
// 1. Bot-Polling stoppen
// 2. Laufende Uploads abwarten (max. 60s Timeout)
// 3. Prozess beenden
// PM2 sendet SIGINT, dann nach Timeout SIGKILL.
```
---
## Erfolgskriterien (Gesamt)
- [ ] `pnpm lint` (tsc --noEmit) ohne Errors
- [ ] `pnpm build` erfolgreich
- [ ] Bot startet und verbindet sich mit Telegram
- [ ] `/start` zeigt Begrüßung
- [ ] `/tenant` zeigt Inline-Keyboard mit Tenants aus der API
- [ ] Tenant-Wechsel funktioniert und wird in Session gespeichert
- [ ] Bild-Upload (als Foto) → Bild erscheint in Payload CMS Media Collection mit korrektem Tenant
- [ ] Bild-Upload (als Dokument) → Bild in Originalqualität hochgeladen
- [ ] Caption wird als Alt-Text übernommen
- [ ] Album-Upload funktioniert (mehrere Bilder)
- [ ] `/list` zeigt letzte 5 Uploads
- [ ] `/status` zeigt API-Status und Token-Validität
- [ ] Nicht-autorisierte User werden blockiert
- [ ] Fehler werden dem User als verständliche Meldungen angezeigt
- [ ] PM2 managed den Prozess mit Auto-Restart
- [ ] Logs werden in `./logs/` geschrieben
---
## Selbst-Prüfung
Nach jeder Iteration:
1. `pnpm lint` (tsc --noEmit)
2. `pnpm build`
3. Bei Fehler: korrigieren und wiederholen
4. Manueller Test-Flow:
- Bot starten mit `pnpm dev`
- `/start` senden
- `/tenant` → Tenant wählen
- Bild senden → Upload prüfen
- `/list` → Upload in Liste sichtbar
5. Fortschritt in README.md dokumentieren
---
## Escape Hatch
Nach 15 Iterationen ohne Fortschritt:
- Dokumentiere was blockiert in BLOCKERS.md
- Liste alle versuchten Ansätze auf
- Schlage 3 alternative Lösungswege vor
- Output <promise>BLOCKED</promise>
---
## Hinweise für die Implementierung
1. **Telegram Bot Token** muss vor dem Start via @BotFather erstellt und in `.env` eingetragen werden.
2. **Payload Admin Credentials** müssen einen User mit SuperAdmin-Rechten referenzieren (für Tenant-übergreifenden Zugriff).
3. **Grammy statt node-telegram-bot-api** Grammy ist moderner, hat bessere TypeScript-Unterstützung und aktive Wartung.
4. **Native fetch statt axios** Node.js 22 hat native fetch/FormData. Keine zusätzliche HTTP-Library nötig.
5. **Long-Polling statt Webhooks** Einfacher zu deployen (kein öffentlicher Endpoint nötig), perfekt für den Use Case.
6. **Kein separater LXC-Container** nötig der Bot läuft als zusätzlicher PM2-Prozess auf sv-payload.
---
## Fertig?
Wenn ALLE Aufgaben erledigt sind UND alle Erfolgskriterien erfüllt sind:
<promise>TELEGRAM_BOT_COMPLETE</promise>