- 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>
22 KiB
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:
curl -X POST "https://cms.c2sgmbh.de/api/users/login" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"password": "your-password"
}'
Response:
{
"message": "Auth Passed",
"user": { "id": 1, "email": "admin@example.com", "isSuperAdmin": true },
"token": "eyJhbGciOiJIUzI1NiIs..."
}
Token verwenden:
curl "https://cms.c2sgmbh.de/api/media" \
-H "Authorization: JWT eyJhbGciOiJIUzI1NiIs..."
Media Upload Endpoint
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 initausgeführt- TypeScript konfiguriert (strict mode)
- Alle Dependencies installiert
.gitignoreenthältnode_modules,.env,dist
1.2 Dependencies installieren
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
{
"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
{
"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
# 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
// 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_IDSwird alsnumber[]geparst
3. Payload API Client
3.1 Authentifizierung mit Token-Caching
Datei: src/payload/client.ts
Implementiere einen Payload API Client mit folgender Logik:
- Login:
POST /api/users/loginmit Email/Password - Token speichern (In-Memory, NICHT auf Disk)
- Token-Expiry tracken: JWT decodieren (ohne Verifikation, nur Payload lesen),
expFeld prüfen - Auto-Refresh: Vor jedem API-Call prüfen ob Token noch gültig (mit 5 Min. Buffer). Falls abgelaufen → neu einloggen.
- Retry-Logik: Bei 401-Response einmal neu einloggen und Request wiederholen.
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
tenantals Feld mitsenden - Bei Reads:
?where[tenant][equals]=<ID>als Query-Parameter - Bei Writes:
tenantim 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
// 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
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
// 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)
// 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)
// 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
// 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
// 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
// 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
// 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
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.
# 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
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
- User-Whitelist: Nur explizit erlaubte Telegram User-IDs dürfen den Bot nutzen
- Token-Sicherheit: Payload JWT wird nur im Memory gehalten, nie auf Disk geschrieben
- Environment Variables: Alle Secrets in
.env, nie im Code - Rate Limiting: Maximal 10 Uploads pro Minute pro User (Bot-seitig implementiert)
- Dateigrößen-Limit: Telegram begrenzt auf 20 MB, zusätzlich Bot-seitiges Limit von 20 MB
- Dateityp-Validierung: Nur erlaubte MIME-Types akzeptieren (image/jpeg, image/png, image/webp, image/avif, image/gif, image/svg+xml)
- Kein Webhook-Modus: Long-Polling vermeidet die Notwendigkeit eines öffentlich erreichbaren Endpoints
7.2 Telegram Bot erstellen
- Öffne Telegram und suche
@BotFather - Sende
/newbot - Name:
CCS Media Upload Bot(oder ähnlich) - Username:
ccs_media_upload_bot(muss eindeutig sein und aufbotenden) - Token sichern → in
.envalsTELEGRAM_BOT_TOKENeintragen - 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
// 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 Errorspnpm builderfolgreich- Bot startet und verbindet sich mit Telegram
/startzeigt Begrüßung/tenantzeigt 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)
/listzeigt letzte 5 Uploads/statuszeigt 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:
pnpm lint(tsc --noEmit)pnpm build- Bei Fehler: korrigieren und wiederholen
- Manueller Test-Flow:
- Bot starten mit
pnpm dev /startsenden/tenant→ Tenant wählen- Bild senden → Upload prüfen
/list→ Upload in Liste sichtbar
- Bot starten mit
- 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 BLOCKED
Hinweise für die Implementierung
- Telegram Bot Token muss vor dem Start via @BotFather erstellt und in
.enveingetragen werden. - Payload Admin Credentials müssen einen User mit SuperAdmin-Rechten referenzieren (für Tenant-übergreifenden Zugriff).
- Grammy statt node-telegram-bot-api – Grammy ist moderner, hat bessere TypeScript-Unterstützung und aktive Wartung.
- Native fetch statt axios – Node.js 22 hat native fetch/FormData. Keine zusätzliche HTTP-Library nötig.
- Long-Polling statt Webhooks – Einfacher zu deployen (kein öffentlicher Endpoint nötig), perfekt für den Use Case.
- 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:
TELEGRAM_BOT_COMPLETE