Zurück zur Startseite

AI-Posts-Verifikation in X mit Puppeteer

Der Artikel analysiert das Problem falscher Erfolge des AI-Agents in X: 83 DOM-Klicks vs 16 echte Posts. Beschreibt die Implementierung der Verifikation via Page-Reload und DOM-Scan zur Extraktion der tweet ID. Nach dem Fix erreichte die Erfolgsrate 94 %. Der Ansatz ist auf alle Plattformen mit stillen Filtern anwendbar.

83 gefälschte AI-Erfolge in X: Wie wir mit Verifikation beheben
Advertisement 728x90

Überprüfung von AI-Agent-Posts auf X: Von 83 Fehlalarmen zu 94 % Echt-Erfolg

Ein AI-Agent für automatisierte Kommentare auf X meldete 83 erfolgreiche Posts, doch nur 16 tauchten tatsächlich auf. Der Grund war eine unvollständige Überprüfung: Ein Klick auf Antworten garantiert nicht, dass der Inhalt auf der Plattform erscheint. Die Spam-Filter von X lassen Beiträge leise verschwinden, ohne jegliche Benachrichtigung.

So funktioniert das Posten über den Browser

Der Agent nutzt Puppeteer CDP, um einen echten Browser zu steuern. Er öffnet den Thread, gibt den Text ein und klickt auf Antworten. Die erste Version der Funktion prüfte nur das DOM-Event:

const button = await page.$('[data-testid="tweetButton"]');
if (button) {  
  const disabled = await button.evaluate(el =>     
     el.getAttribute('aria-disabled') === 'true'  
  );  
  if (!disabled) {    
    await button.click();
    return true; // <-- Problem: zu früher Erfolg  
  }
}
return false;

Ein true-Rückgabewert markierte es als Erfolg auf DOM-Ebene, ohne Bestätigung der Plattform. Die Datenbank füllte sich mit Einträgen, und das Dashboard leuchtete grün.

Google AdInline article slot

X signalisiert Ablehnungen nicht: Das Feld leert sich, die URL bleibt gleich. Rate-Limits oder Inhaltsrichtlinien greifen stumm, und der Browser täuscht sich über den Erfolg.

Probleme mit Post-IDs

Zur Nachverfolgung holte der Agent die ID aus page.url() nach dem Klick:

const tweetId = page.url().split('/status/')[1];

X leitet nicht zum neuen Post weiter – die URL bleibt die des Eltern-Posts. Alle 83 Einträge bekamen dieselbe parent_tweet_id, was das Problem hinter scheinbar gültigen Daten versteckte.

Google AdInline article slot

Das Problem kam manuell über X-Analytics ans Licht: 83 gemeldet vs. 16 echte Antworten.

Ursache: Lücke zwischen DOM und Plattform

DOM: button.click() → Erfolg ✓
Browser: Event ausgelöst → Erfolg ✓
X-Plattform: Inhalt verworfen → ??? (Stille)
Datenbank: als "gepostet" gespeichert → Erfolg ✓
Dashboard: 83 Engagements → super ✓
Realität: 16 tatsächliche Posts

Die Überwachung endete beim Browser. Plattform-Filter sind eine Blackbox.

Lösung: Post-Überprüfung mit Seitenneuladen

Die neue Funktion verifyReplyPosted wartet auf das Rendering, lädt die Seite neu und durchsucht das DOM nach dem Text:

Google AdInline article slot
export async function verifyReplyPosted(  
  page: Page,  
  replyText: string,  
  parentTweetId: string,
): Promise<{ verified: boolean; replyTweetId: string | null }> {
  
  await new Promise(r => setTimeout(r, 3000));  
  await page.reload({ waitUntil: "domcontentloaded" });  
  await new Promise(r => setTimeout(r, 5000));
  
  await page.evaluate("window.scrollBy(0, 1000)");  
  await new Promise(r => setTimeout(r, 2000));
  
  const result = await page.evaluate(`    
     (function() {      
         var snippet = '${safe}'; // erste 30 Zeichen      
         var articles = document.querySelectorAll(        
           'article[data-testid="tweet"]'      
         );
         for (var i = 0; i < articles.length; i++) {        
             var textEl = articles[i].querySelector(          
                 '[data-testid="tweetText"]'        
             );    

             if (!textEl) continue;        
             var text = (textEl.innerText || '').slice(0, 60);

             if (text.indexOf(snippet.slice(0, 30)) !== -1) {

                 var links = articles[i].querySelectorAll(            
                   'a[href*="/status/"]'          
                 );          

                 return { verified: true, replyTweetId: realId };        
               }      
         }            

         return { verified: false, replyTweetId: null };    
     })()  
 `);    
  return result;
}
  • Neuladen sorgt für einen frischen Thread ohne Cache.
  • Suche über article[data-testid="tweet"] und [data-testid="tweetText"].
  • ID aus href-Links im Article extrahiert.
  • Nur verified: true wird in die Datenbank geloggt.

Warum dieser Ansatz überall funktioniert

Das ist nicht X-spezifisch. Plattformen (Social Media, Formulare, Zahlungen, Deployments) lehnen Inhalte leise ab.

Wichtige Verifizierungsprinzipien:

  • Echten Nutzer nachahmen: Neuladen + Scrollen für Lazy Loading.
  • Nach einem Marker (Text-Snippet) im Ziel-DOM suchen.
  • IDs aus realen Elementen ziehen, nicht aus URLs oder Events.
  • Ohne Beweis nicht loggen.
  • Fehlschläge in Echtzeit protokollieren.

Wichtige Erkenntnisse

  • DOM-Events ≠ Plattform-Erfolg: Ein Klick garantiert kein Posten.
  • Stille Ablehnungen: Spam-Filter arbeiten ohne Warnungen, um Umgehungen zu verhindern.
  • DOM-Scan-Verifizierung: Neuladen + Text/ID-Suche.
  • Nach der Korrektur: 94 % verifizierte Erfolgsrate, Fehlschläge sofort sichtbar.
  • Breite Anwendbarkeit: Jede automatisierte Post- oder Submit-Funktion braucht nutzerähnliche Checks.

Nach der Umsetzung trackt der Agent echte Posts, das Dashboard zeigt die Wahrheit. Die Methode skaliert auf andere Plattformen mit ähnlichen Filtern.

— Editorial Team

Advertisement 728x90

Weiterlesen