Communitygithub.com

jvdburgh/AmputatorBot

AmputatorBot-Migrationsplan v7 — festgelegte Entscheidungen, Meilensteine M1–M6, API-Vertrag, Schema, Portierungsnotizen. Laden, wenn an der AmputatorBot-Architektur, dem Rewrite oder der M6-Bereitstellung gearbeitet wird.

Was ist AmputatorBot?

Der AmputatorBot-Migrationsplan v7 beschreibt den vollständigen Übergang des bestehenden Reddit-Bots (Python + PRAW) zu einer modernen Architektur, die TypeScript (Devvit), Rust (Axum) und Astro 5 umfasst. Er definiert eine klare Strategie mit sechs Meilensteinen (M1–M6), wobei der alte Bot während der Entwicklung (M1–M5) parallel läuft und erst bei M6 abgeschaltet wird. Der Plan deckt alle technischen Entscheidungen ab, wie die Verwendung von `dom_smoothie` für die Artikel-Extraktion, den Wechsel zu Postgres 17 auf Scaleway, den Verzicht auf Authentifizierung und die Nutzung von Cloudflare für Ratenbegrenzung und DDoS-Schutz. Entwickler und KI-Agenten, die an diesem Projekt arbeiten, erhalten eine vollständige Referenz zu den Locked Decisions, der API-Architektur (v1/v2), dem Datenbankschema und den Arbeitsabläufen, einschließlich der expliziten Anweisung, vor jeder architektonischen Entscheidung Rücksprache zu halten.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/jvdburgh/AmputatorBot/tree/HEAD/.claude/skills/amputatorbot-migration

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

AmputatorBot → Devvit-Migrationsplan v7

Status: lebendiger Entwurf — M1–M5 ausgeliefert, M6 (Cloud-Deploy + Launch) in Arbeit. Erstellt: 2026-05-25 Budgetobergrenze: 15 €/Monat gesamt


Arbeitsweise — BITTE ZUERST LESEN

Dev möchte vor dem Schreiben von Code zu Architekturentscheidungen konsultiert werden. Für jede Wahl, die:

  • Bibliothek, Framework oder Muster betrifft
  • Datenmodell-Design oder Schema-Entscheidungen betrifft
  • Die Struktur eines neuen Moduls oder Layers betrifft
  • Kosten- vs. Komplexitäts-Kompromisse betrifft
  • In nicht-trivialer Weise von diesem Plan abweicht

…anhalten und vorher fragen. Die Optionen darlegen, die Kompromisse ehrlich erklären (einschließlich Widerspruch, falls ihr nicht einverstanden seid), und auf eine Entscheidung warten. Mechanische Implementierungsarbeit benötigt keine Konsultation; architektonische Form schon.

Externe Aktionen, die Dev manuell ausführt, nicht Claude:

  • Abfragen gegen die Produktions-MySQL / Postgres-Datenbanken ausführen (Claude kann das SQL schreiben; Dev führt es aus)
  • Dienste anmelden (Scaleway, Reddit-Entwicklerkonto, App Directory-Einreichung, Prämienprogramm-Registrierung)
  • Alles, was ein externes Konto mit Devs Identität betrifft (OAuth-Logins, devvit login, scw init, Änderungen an der Abrechnung)
  • DNS-Änderungen in Cloudflare
  • Stoppen/Starten des Produktions-PythonAnywhere-Bots
  • git push zu produktionsrelevanten Remotes
  • Veröffentlichen der Devvit-App (devvit upload --publish)

Claude bereitet die genauen Befehle vor und sagt Dev, wann sie ausgeführt werden sollen. Code lesen, Code schreiben, lokale Tests, lokale Docker-Builds, lokales cargo/npm — Claude erledigt das.


Projektkontext

Was AmputatorBot ist: Ein Reddit-Bot, der AMP-URLs in Kommentaren/Posts/PMs erkennt und mit dem kanonischen (Nicht-AMP) Link antwortet. Wird von Dev (u/Killed_Mufasa) gewartet. Live unter https://github.com/jvdburgh/AmputatorBot. ~181 GitHub-Sterne. Läuft in großem Maßstab. Das Reddit Developer Platform-Migrationsprogramm bietet 1.000 $ für Bots, die bis zum 31. Dezember 2026 von der Data API zu Devvit migrieren.

Was migriert wird:

  • Reddit-Bot (Python + PRAW) → Devvit-App (TypeScript, @devvit/web)
  • HTTP-API (Flask) → Rust (Axum + Mozilla-Readability-Port + sqlx)
  • Website (Flask + Bootstrap) → Astro 5 + Tailwind 4 + shadcn/ui
  • Datenbank (MySQL auf PythonAnywhere) → Postgres 17 auf Scaleway Managed Database
  • Host (PythonAnywhere) → Scaleway (Paris, Frankreich — EU-verwaltete Cloud)

Änderung des Architekturmodells: Devvit ist pro-Subreddit-opt-in, nicht global. Mods installieren die App pro Sub; sie kann keine Inhalte sehen, wo sie nicht installiert ist. Antworten werden als Pro-Installations-App-Identität gepostet, nicht als u/AmputatorBot. Akzeptiert. Geschätzter Rückgang des Antwortvolumens: 30–60 %.

Ziele (vom Benutzer angegeben):

  1. Moderner Stack, dabei lernen (Rust, Devvit, Astro/Tailwind4/shadcn)
  2. API-Vertrag bleibt abwärtskompatibel
  3. Während des Rewrites (M1–M5) lief der alte Bot auf PythonAnywhere weiter, damit die neue Seite frei brechen konnte, ohne dass es für den Benutzer Konsequenzen hatte. Der alte Bot wird zum M6-Cutover stillgelegt — kein permanenter Parallelbetrieb.
  4. Mit dem Vorschlaghammer durch den Rewrite; alten Code in praw-python-archive/ aufbewahren

Festgelegte Entscheidungen

PfadPro-Subreddit-Opt-in. Direkter Cutover bei M6 (alter Bot während der M1–M5-Entwicklung nur am Leben gehalten)
Devvit SDK@devvit/web (modernes Servermodell)
Devvit-Konfigurationdevvit.json (offizielles Format mit $schema)
Devvit-Gerüstreddit/devvit-template-bare, ohne Post/Menü-UI
Backend-SpracheRust
Backend-FrameworkAxum 0.8 (Tokio + Hyper)
Artikel-Extraktiondom_smoothie Crate (≈0.17, Rust Readability-Port, der Mozillas readability.js genau folgt, aktiv gewartet seit 2026-03)
HTML-Parsingscraper Crate (basiert auf html5ever)
HTTP-Clientreqwest
Postgres-Treibersqlx (zur Kompilierzeit geprüfte Abfragen)
DatenbankPostgres 17 auf Scaleway Managed Database (DB DEV-S oder nächsthöhere Stufe, dimensioniert für ~2 GB + Wachstumsreserve). 1,7 Mio. Zeilen bestätigt.
WebsiteAstro 5 + Tailwind 4 + shadcn/ui
HostingScaleway Serverless Containers (Paris/AMS, EU-ursprüngliche französische Cloud)
Single ServiceEine Rust-Binärdatei bedient /api/* und Astro-Statik via tower-http::ServeDir
ObservabilityZunächst nur Scaleway Cockpit (integrierte Logs + Metriken)
Code-HostingGitHub (bestehendes Monorepo)
DNSCloudflare (bestehend) — übernimmt auch Ratenbegrenzung
API-AuthentifizierungKeine auf irgendeiner Route — vollständig offen. Cloudflare behandelt Missbrauch.
RatenbegrenzungNur Cloudflare, keine Rust-Middleware
DomainSingle — alles auf www.amputatorbot.com
Test-Subredditr/test
KI-ToolingClaude Code + Devvit MCP + JetBrains MCP in IntelliJ
Pro-Benutzer-Opt-outGestrichen. Benutzer blockieren das Bot-Konto; die Pro-Installations-App-Identität macht blockbasiertes Opt-out auf Installationsebene funktionsfähig.
Pro-Subreddit-Opt-outGestrichen (Mods deinstallieren oder schalten autoReply in den Installationseinstellungen aus)
Modmail / DMsKeine. Kein eingehender Modmail-Handler, keine Willkommens-Modmail, keine DMs — Reddits "App"-Abzeichen deckt die Bot-Offenlegung ab, und der Devvit App Directory-Eintrag deckt das Onboarding ab.
Karma-SchwelleKeine
App Directory-Einreicheru/Killed_Mufasa

Warum diese Entscheidungen

Die Begründung für die obigen Entscheidungen. Jede war eine bewusste Wahl gegenüber einer glaubwürdigen Alternative.

  • Rust + Axum statt Node/TS oder Python. Einzelne statische Binärdatei, keine Laufzeit, kein virtualenv, kein node_modules. Die schwierigen Teile von Rust (Lebensdauern, unsicher, fortgeschrittene Generics) tauchen bei dieser Art von Dienst (HTTP-Abruf + Regex + HTML-Abfrage + DB-Schreibvorgang) kaum auf, daher ist die Lernkurve hauptsächlich Result/?, async/Tokio und sqlx-Makros — alles wissenswert. Axum 0.8 ist das Konsens-Framework im Jahr 2026 (Tokio + Hyper + Tower-Middleware). sqlx gibt zur Kompilierzeit geprüfte Abfragen.

  • dom_smoothie statt rs-trafilatura und anderen Readability-Ports. Der bestehende Bot verwendet newspaper3k + difflib.SequenceMatcher für Raten-und-Prüfen-Konfidenz-Scoring (helpers/article_comparer.py). Für den Rust-Port wollen wir einen Mozilla-Readability-konformen Extraktor. Die Kandidaten ab Mai 2026:

    • dom_smoothie 0.17 (letzte Veröffentlichung 2026-03, ~40k aktuelle Downloads, Repo niklak/dom_smoothie) — folgt readability.js genau, zwei Modi (Mozilla-Nachahmung + benutzerdefiniert), JSON-LD + OG-Metadaten-Parsing, aktiv gewartet.
    • readability 0.3 von kumabook (letzte Veröffentlichung 2023-12, stagniert) — Port der ursprünglichen arc90-Readability (vor Mozillas Fork). Höhere historische Downloadzahl, aber ungewartet.
    • readable-readability 0.4 (letzte Veröffentlichung 2022-12, stagniert).
    • llm_readability 0.0.17 (aktiv, aber 0.0.x — zu früh für die Produktion).

    dom_smoothie gewinnt aufgrund aktiver Wartung + expliziter Mozilla readability.js-Abstammung. Wir verwenden seinen Standard (Mozilla-Nachahmungs)-Modus für Ähnlichkeitsbewertung. Kein Spike nötig; in M2 auf dom_smoothie = "0.17" festlegen.

  • Keine Authentifizierung, nirgendwo. Die Produktion hat derzeit authorization_required=False auf /convert (AmputatorBotCom/main.py:162). Niemand ist auf Bearer-Auth angewiesen, und nachdem das Opt-out weg ist, hat das Backend überhaupt keine privilegierten Routen mehr. Cloudflare behandelt Missbrauch.

  • Ratenbegrenzung auf Cloudflare-Seite. Cloudflare ist bereits vor der Domain. Per-IP-Regeln dort sind kein Rust-Code und ohne erneuten Deploy konfigurierbar.

  • Kein Opt-out-Mechanismus, keine Modmail, keine DMs. Benutzer und Mods, die den Bot nicht wollen, können die Pro-Installations-App-Identität blockieren bzw. die App deinstallieren. Das ist genug — darüber hinaus ein separates Opt-out-Register zu bauen, ist ein Mechanismus, den wir nicht brauchen. Schneidet den Modmail-Trigger, die Willkommens-Modmail, die Bearer-Auth-privilegierten Routen, die optouts-Tabelle, den Redis-Opt-out-Cache und den Legacy-"per DM herbeigerufen"-Pfad auf einen Schlag ab.

  • Datenbankschema während der Migration modernisieren. Das Migrationstool läuft einmal. Das Bereinigen von VARCHAR(4000) → TEXT, das Hinzufügen der Indizes, die models/table.py fehlen, und das Entfernen toter Spalten ist günstig, es einmal zu tun und nie wieder.

  • Zunächst nur Logs-Observability. Scaleway Cockpit (der integrierte Log- + Metrik-Stream) reicht aus, um zu greppen, wenn Probleme auftauchen. Füge Sentry/Bugsink nur hinzu, wenn Log-Grep schmerzhaft wird.

  • Die bestehenden AMP-Erkennungs- + kanonischen Methoden portieren, aber Fehlalarme an Ort und Stelle beheben. Die 14 Substring-Muster (static/static.py) sind verrauscht (& HTML-Entitäten, lamp_post, streaming_amplifier). Die 10 kanonischen Methoden und ihre Reihenfolge in helpers/canonical_methods.py sind gut abgestimmt und bleiben. Die Verbesserungen erfolgen auf der AMP-Erkennungsebene (Wortgrenzen, Bereichsbegrenzung auf URL-Komponenten nach der Extraktion) und in Fast-Path-Allowlists für High-Signal-Marker.

  • Bot-Antwort-Markdown auffrischen. Einmaliger Design-Durchlauf für eine neue Vorlage, angewendet bei der Migration. Muss nicht identisch mit dem alten Bot aussehen.

  • git mv alles Alte in praw-python-archive/ in einem Commit. Vorschlaghammer. Alter PRAW-Bot läuft während M1–M5 auf PythonAnywhere weiter, damit die neue Seite frei brechen kann; er wird zum M6-Cutover gestoppt.

  • Sechs Meilensteine, kein Kalender. Lokal-zuerst: M1–M5 sind alle lokale Entwicklung (Gerüst, kanonische Engine, API-Parität, Astro, Devvit-Playtest). M6 ist der Cloud- + öffentliche Launch (Scaleway-Bereitstellung, Produktionsdatenmigration, DNS-Cutover, App Directory, Alter-Bot-Ruhestand). Keine externe Frist (Prämie ist Dezember 2026). Alter Bot deckt die Produktion bis zum Cutover ab. Rust + Devvit + Astro zu lernen ist Teil des Ziels, daher sollte das Tempo dem Verständnis entsprechen, nicht einem Zeitplan.


Architektur

┌─────────────────────────────────────────────┐
│ Subreddit X (Mod hat amputatorbot installiert)│
│   Kommentare / Posts                        │
└──────────────┬──────────────────────────────┘
               │ Devvit-Trigger (HTTP POST zu internen Routen)
               ▼
┌─────────────────────────────────────────────┐
│ Devvit-App (Node, @devvit/web)              │
│   Läuft auf Reddit-Infrastruktur            │
│                                             │
│   Interne HTTP-Routen:                      │
│     /internal/triggers/comment-submit       │
│     /internal/triggers/post-submit          │
│                                             │
│   Devvit Redis (integriert):                │
│     handled:{commentId}  TTL 1h             │
│                                             │
│   Pro-Installationseinstellungen:           │
│     autoReply, customFooter, killSwitch     │
│                                             │
│   Allowlist (devvit.json):                  │
│     http.domains: ["www.amputatorbot.com"]  │
└──────────────┬──────────────────────────────┘
               │ HTTPS (keine Authentifizierung):
               │   GET /api/v1/convert?q=...&gac=true&md=3
               ▼
┌─────────────────────────────────────────────┐
│ Cloudflare (DNS + Ratenbegrenzung + WAF)    │
└──────────────┬──────────────────────────────┘
               ▼
┌─────────────────────────────────────────────┐
│ Scaleway (Paris/AMS, EU)                    │
│ www.amputatorbot.com                        │
│                                             │
│   Rust-Backend (Axum 0.8) — einzelne Binär  │
│     /api/v1/convert    (bestehender Vertrag)
│     /api/v2/convert    (camelCase JSON)     │
│     /api/v2/stats      (zwischengespeicherter Zähler)│
│     /api/v2/health                          │
│     Fallback → Astro-Statik (ServeDir)      │
│                                             │
│   Kanonische-Suche (10 Methoden, gleiche    │
│   Reihenfolge wie Python-Impl.):            │
│     REL, CANURL, OG_URL,                    │
│     GOOGLE_MANUAL_REDIRECT, GOOGLE_JS_…,    │
│     BING_ORIGINAL_URL, SCHEMA_MAINENTITY,   │
│     TCO_PAGETITLE, META_REDIRECT,           │
│     GUESS_AND_CHECK (Artikel-Ähnlichkeit-   │
│       Konfidenz via Mozilla Readability),   │
│     DATABASE (Cache-Lookup)                 │
│                                             │
│   Postgres 17 (Scaleway Managed Database):  │
│     links                                   │
└─────────────────────────────────────────────┘

Warum einzelner Rust-Dienst, der API + statische Website bedient: Devvits Allowlist ist auf Domain-Ebene. Ein Scaleway Serverless Container hält die Deploy-Geschichte einfach. tower-http::ServeDir als Fallback sind ~5 Zeilen.


API-Vertrag

Zwei Endpunkte werden parallel bereitgestellt:

  • GET /api/v1/convert — Legacy-Vertrag beibehalten (Snake_Case Query-String, Snake_Case JSON-Antwort). Nur GET, da der Legacy-Bot immer query-string-basiert war; POST ist nur v2. Für neue Aufrufer veraltet, aber auf unbestimmte Zeit unterstützt, damit externe Dienste nicht brechen.
  • POST /api/v2/convert — Moderner Vertrag: JSON-Body in CamelCase, JSON-Antwort in CamelCase. entryType ist ein Body-Feld statt eines Headers. Strenge Validierung (unbekannte Felder → 422). Empfohlen für Devvit (M5) + Website (M4) + neue externe Aufrufer.

Jeder Schreibvorgang in den links-Cache zeichnet api_version (1 oder 2 — NULL für die vor-v7-Legacy-Bot-CSV-importierten Zeilen) auf, damit die Adoption abfragbar ist: SELECT api_version, COUNT(*) FROM links GROUP BY 1.

v1 — GET /api/v1/convert (aus AmputatorBotCom/main.py:161+)

Query-Parameter:

  • q (erforderlich) — URL oder Text, der URLs enthält
  • gac — Raten-und-Prüfen, Standard true
  • md — maximale Tiefe, Standard static.MAX_DEPTH (Wert portieren)
  • gc — Antwort-Markdown in der Antwort generieren, Standard false
  • r — Weiterleitungsmodus (303 zur kanonischen URL statt JSON-Antwort), Standard false

URL-Kodierungsverhalten — beide müssen funktionieren

Die API unterstützt zwei Aufrufer: wohlverhaltene Clients, die ihre URLs Prozent-kodieren, und Menschen, die rohe URLs in die Adressleiste des Browsers einfügen. Der v7-Vertrag:

  1. Kodierte URLs funktionieren immer, unabhängig von der Position von q in der Parameterliste. ?q=https%3A%2F%2F...&gac=true und ?gac=true&q=https%3A%2F%2F... sind beide gültig.
  2. Unkodierte URLs funktionieren, wenn q der letzte Parameter ist. ?gac=true&q=https://example.eu/article?id=1 ist in Ordnung — die Tail-Parameter der URL (?id=1) bleiben erhalten. q zwischen anderen bekannten Parametern ist …

Verwandte Skills