Twitter-Fähigkeit (Skill)
Ermöglicht das Posten von Text und Bildern auf X (Twitter) mittels Chrome Computer Use Mode.
Skriptverzeichnis
Wichtig: Alle Skripte befinden sich im scripts/-Unterverzeichnis dieser Fähigkeit.
Anweisungen für die Agentenausführung:
- Ermitteln Sie den Verzeichnispfad dieser SKILL.md-Datei als
{baseDir} - Skriptpfade:
- Zwischenablage-Helfer:
{baseDir}/scripts/copy-to-clipboard.ts - Tweet-Exporter/-Scraper:
{baseDir}/scripts/export-tweets.ts - Offizieller X-API-Helfer:
{baseDir}/scripts/x-api.ts
- Zwischenablage-Helfer:
- Ermitteln Sie die
${BUN_X}-Laufzeit: fallsbuninstalliert →bun; fallsnpxverfügbar →npx -y bun; andernfalls Installation von bun vorschlagen - Ersetzen Sie alle
{baseDir}und${BUN_X}in diesem Dokument durch die tatsächlichen Werte
Ausführungsmodus
Diese Fähigkeit folgt einem Hybriden Ausführungsmodell:
- API zuerst (Empfohlen für Textbeiträge): Versuchen Sie, den Tweet mit dem offiziellen X-API-Skript (
x-api.ts) zu posten. Dies ist schnell, stabil und verbraucht keine Browser-Ressourcen. - Browser-Fallback: Falls der API-Aufruf fehlschlägt (z.B. monatliches 1500-Tweet Free-Tier-Kontingent überschritten, Rate-Limit) oder API-Anmeldedaten nicht in
subscription.yamlkonfiguriert sind, wechseln Sie automatisch in den Chrome Computer Use Mode / CLI Bridge, um Browser-Aktionen zu simulieren.
Voraussetzungen
- Für den API-Modus: Konfigurieren Sie Ihre X-API-Anmeldedaten (OAuth 1.0a User Context Tokens) in Ihrer lokalen
subscription.yaml-Datei (die git-ignoriert ist):x_api: api_key: "IHR_API_SCHLÜSSEL" api_key_secret: "IHR_API_SCHLÜSSEL_GEHEIMNIS" access_token: "IHR_ZUGRIFFSTOKEN" access_token_secret: "IHR_ZUGRIFFSTOKEN_GEHEIMNIS" - Für den Browser-Fallback: Google Chrome installiert, in Chrome bei X (Twitter) angemeldet und, falls erforderlich, macOS-Barrierefreiheitsberechtigungen erteilt.
Arbeitsablauf für reguläre Beiträge (Text & Bilder)
Bei der Ausführung eines Beitrags:
- Starten Sie den Agenten-Durchgang mit dem Aufruf von
get_app_state(oder einem entsprechenden Werkzeug) fürGoogle Chrome. - Öffnen Sie Google Chrome oder navigieren Sie zu
https://x.com/compose/post. - Lokalisieren Sie das Eingabefeld des Tweet-Verfassers.
- Geben Sie den Beitragstext mithilfe von Tastatureingaben des Computer Use in den Verfasser ein.
- Falls Bilder angehängt werden sollen (max. 4):
Für jedes Bild:
a. Führen Sie das Zwischenablage-Helfer-Skript aus, um das Bild in die Zwischenablage zu kopieren:
b. Fügen Sie das Bild mit der Tastenkombination zum Einfügen (${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts image /absoluter/pfad/zu/bild.pngsuper+vauf macOS,control+vauf Windows/Linux) in den Verfasser ein. c. Warten Sie 2-3 Sekunden, bis X das Hochladen der Medien abgeschlossen hat. - Veröffentlichungssicherheit: Klicken Sie niemals auf
Veröffentlichen,Postenoder eine entsprechende Schaltfläche, um den Tweet zu veröffentlichen, ohne vorher eine explizite endgültige Bestätigung des Benutzers im aktuellen Gespräch eingeholt zu haben. - Sobald der Benutzer bestätigt hat, klicken Sie zum Veröffentlichen auf die Schaltfläche
Posten. - Schließen Sie nach dem Veröffentlichen das Verfasser-Modal, damit die Benutzeroberfläche nicht im Verfassen-Dialog stecken bleibt. Verwenden Sie die Schaltfläche zum Schließen oder die Escape-Taste:
- DOM-Selektor:
[data-testid="app-bar-close"]oder[aria-label="Schließen"] - Fallback: Senden Sie ein
Escape-keydown-Ereignis
- DOM-Selektor:
- Automatisches Neuladen des Feeds (Optional): Falls der Benutzer andere Tabs mit seinem Profil (z.B.
x.com/[benutzername]) oder dem Start-Feed (x.com/home) geöffnet hat, laden Sie diese neu, damit der neue Tweet sofort sichtbar ist.
var closeBtn = document.querySelector('[data-testid="app-bar-close"], [aria-label="Schließen"]');
if (closeBtn) { closeBtn.click(); }
else { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true })); }
CLI Bridge (Keine Computer Use Werkzeuge)
Wenn die Umgebung keine Computer Use Tastatur-/Mauswerkzeuge bereitstellt, verwenden Sie plattformspezifische Methoden, um Chrome zu öffnen und JavaScript in die Seite zu injizieren.
macOS (AppleScript)
Verfasser-Seite öffnen:
open -a "Google Chrome" "https://x.com/compose/post"
JavaScript in Chrome ausführen — schreiben Sie JS zuerst in eine temporäre Datei (vermeidet Probleme mit Shell-Escaping) und führen Sie es dann über AppleScript aus:
cat > /tmp/tweet.js << 'EOF'
(function() { /* Ihr Code */ })();
EOF
osascript -e '
tell application "Google Chrome"
activate
set js to read "/tmp/tweet.js"
set result to execute front window''''s active tab javascript js
return result
end tell'
Muster: Schreiben Sie die JS-Nutzlast immer zuerst in eine temporäre Datei. Versuchen Sie NICHT, osascript -e mit eingebettetem JS inline zu verwenden — Anführungszeichen-/Escaping-Konflikte führen zu Parsing-Fehlern.
Verfasser nach dem Veröffentlichen schließen (andernfalls bleibt das Modal geöffnet):
var closeBtn = document.querySelector('[data-testid="app-bar-close"], [aria-label="Schließen"]');
if (closeBtn) { closeBtn.click(); }
else { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true })); }
Profil-/Startseiten-Tabs neu laden, um den neuen Beitrag sofort anzuzeigen (Optional):
osascript -e '
tell application "Google Chrome"
tell window 1
set tabList to every tab whose URL contains "x.com/benutzername" or URL contains "x.com/home"
repeat with t in tabList
reload t
end repeat
end tell
end tell'
Linux
google-chrome "https://x.com/compose/post"
# Verwenden Sie chrome-remote-interface oder einen ähnlichen CDP-Client, um JS auszuführen
Windows
Start-Process "chrome" "https://x.com/compose/post"
# Verwenden Sie CDP über --remote-debugging-port oder PowerShell-Automatisierung
JavaScript / DOM-Automatisierungsrichtlinien (Fallback)
Wenn direkte Computer Use Tastatur-/Mausereignisse nicht verfügbar sind oder Sie im Browser-Scripting-/CDP-Modus arbeiten, befolgen Sie diese Richtlinien zur Interaktion mit X's React/Draft.js-Editor:
-
Erkennung mehrerer Editoren: X-Seiten enthalten oft mehrere Verfasser-Textbereiche (z.B. inline-Verfasser im Start-Feed und aktives Modal-Dialogfeld). Fragen Sie immer alle Editoren ab:
var els = document.querySelectorAll('[data-testid="tweetTextarea_0"]'); var el = els.length > 1 ? els[els.length - 1] : els[0];Zielen Sie immer auf den aktiven Modal-Verfasser (normalerweise das letzte Element in der Liste).
-
Bindungsauswahl & Fokus: Bevor Sie Text einfügen, MÜSSEN Sie auf das Element klicken, um die Draft.js-Auswahlbindung auszulösen, und dann den Fokus setzen:
el.click(); el.focus(); -
Erhaltung der Editorstruktur:
- Verwenden Sie NICHT
el.innerHTML = ''oderdocument.execCommand('delete')bei einem leeren Verfasser. Das Löschen der DOM-Knoten zerstört die interne Wrapper-Struktur von Draft.js (z.B. denpublic-DraftStyleDefault-block-Span), was die React-Komponente zum Absturz bringt und die Posten-Schaltfläche dauerhaft deaktiviert. - Verwenden Sie stattdessen
document.execCommand('insertText', false, text)direkt im leeren, fokussierten Editor.
- Verwenden Sie NICHT
-
Auslösen von React-Zustandsaktualisierungen: Senden Sie nach dem Texteinfügen ein bubbles
input-Ereignis, um React zu benachrichtigen:el.dispatchEvent(new Event('input', { bubbles: true })); -
Auffinden der korrekten Posten-Schaltfläche: Die Test-IDs der Schaltflächen (
tweetButtonInlineundtweetButton) können je nach Kontext vertauscht sein. Suchen Sie immer nach der sichtbaren, aktivierten Schaltfläche:var btns = document.querySelectorAll('[data-testid="tweetButtonInline"], [data-testid="tweetButton"]'); var activeBtn = Array.from(btns).find(function(btn) { var isVisible = btn.offsetWidth > 0 && btn.offsetHeight > 0; var isDisabled = btn.disabled || btn.getAttribute('aria-disabled') === 'true'; return isVisible && !isDisabled; }); if (activeBtn) activeBtn.click();
Arbeitsablauf zum Löschen eines Tweets
Bei der Ausführung einer Löschung:
- Öffnen Sie Google Chrome oder navigieren Sie zur Profilseite des Benutzers (
https://x.com/[benutzername]) oder zur direkten Tweet-URL (https://x.com/[benutzername]/status/[tweetId]). - Suchen Sie den Ziel-Tweet
<article>-Container, der den zu löschenden Text enthält. - Klicken Sie auf die Optionsmenü-Schaltfläche des Tweets:
- DOM-Selektor:
[data-testid="caret"]
- DOM-Selektor:
- Warten Sie 1-2 Sekunden, dann klicken Sie auf den Menüpunkt "Löschen":
- DOM-Selektor: Ein
[role="menuitem"]-Element, dessen Text "Löschen" oder "Delete" enthält.
- DOM-Selektor: Ein
- Warten Sie 1-2 Sekunden, dann klicken Sie im Dialogfeld auf die Bestätigungsschaltfläche zum Löschen:
- DOM-Selektor:
[data-testid="confirmationSheetConfirm"](oder Fallback auf eine beliebige Dialogs chaltfläche mit dem Text "Löschen" oder "Delete").
- DOM-Selektor:
CLI Bridge Beispiel (macOS)
Verwenden Sie dasselbe Muster mit temporärer Datei und AppleScript wie beim Posten. Ersetzen Sie TWEET_TEXT_HERE durch den Inhalt des Ziel-Tweets.
Schritt 1 — Tweet finden und Caret klicken:
cat > /tmp/del-1.js << 'EOF'
(function() {
var articles = document.querySelectorAll('article');
var target = null;
var needle = 'TWEET_TEXT_HERE';
for (var i = 0; i < articles.length; i++) {
var textEl = articles[i].querySelector('[data-testid="tweetText"]');
if (textEl && textEl.textContent.trim().indexOf(needle) !== -1) {
target = articles[i];
break;
}
}
if (!target) return 'ERROR: tweet not found';
var caret = target.querySelector('[data-testid="caret"]');
if (!caret) return 'ERROR: caret not found';
caret.click();
return 'OK: caret clicked';
})();
EOF
osascript -e '
tell application "Google Chrome"
set js to read "/tmp/del-1.js"
set result to execute front window''''s active tab javascript js
return result
end tell'
Schritt 2 — Menüpunkt "Löschen" klicken (1-2s nach Schritt 1 warten):
cat > /tmp/del-2.js << 'EOF'
(function() {
var items = document.querySelectorAll('[role="menuitem"]');
for (var i = 0; i < items.length; i++) {
var txt = items[i].textContent.trim();
if (txt === 'Delete' || txt === '删除') {
// Klicken Sie auf das Menüelement selbst oder, falls es ein verschachteltes klickbares Element enthält
var clickTarget = items[i].querySelector('[role="menuitem"]') || items[i];
clickTarget.click();
return 'OK: delete clicked (index ' + i + ')';
}
}
// Fallback: erstes Menüelement klicken (im Dropdown immer "Löschen")
var first = document.querySelectorAll('[role="menuitem"]')[0];
if (first) { first.click(); return 'OK: first menuitem clicked'; }
return 'ERROR: no menuitems';
})();
EOF
osascript -e '
tell application "Google Chrome"
set js to read "/tmp/del-2.js"
set result to execute front window''''s active tab javascript js
return result
end tell'
Schritt 3 — Löschung bestätigen (1-2s nach Schritt 2 warten):
cat > /tmp/del-3.js << 'EOF'
(function() {
var confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
if (!confirmBtn) {
var buttons = document.querySelectorAll('[role="button"], button');
for (var i = 0; i < buttons.length; i++) {
var txt = buttons[i].textContent.trim();
if (txt === 'Delete' || txt === '删除') {
confirmBtn = buttons[i];
break;
}
}
}
if (!confirmBtn) return 'ERROR: confirm button not found';
confirmBtn.click();
return 'OK: confirm clicked';
})();
EOF
osascript -e '
tell application "Google Chrome"
set js to read "/tmp/del-3.js"
set result to execute front window''''s active tab javascript js
return result
end tell'
Arbeitsablauf zum Exportieren von Tweets
Um alle oder gefilterte Tweets von Ihrer Profilseite zu exportieren/zu speichern:
- Führen Sie das Exporter-Skript aus:
${BUN_X} {baseDir}/scripts/export-tweets.ts [startDatum] [endDatum]- Optionale Datumsfilter: Sie können
startDatum(z.B.2026-06-01) undendDatum(z.B.2026-06-30) übergeben, um die Ausgabe nach Datumsbereich zu filtern. Wenn weggelassen, werden alle gescrapten Tweets exportiert. - Ausgabedatei: Die Tweets werden in
/Users/alex/twitter/twitter.yamlgespeichert, wobei die Tweet-URL der Schlüssel und der Tweet-Text der Wert ist.
- Optionale Datumsfilter: Sie können