Que fait Compétence Twitter ?
Publie du texte et des images sur X (Twitter) via le mode d'utilisation de Chrome Computer Use.
Répertoire des scripts
Important : Tous les scripts se trouvent dans le sous-répertoire scripts/ de cette compétence.
Instructions d'exécution pour l'agent :
- Déterminez le chemin du répertoire de ce fichier SKILL.md comme
{baseDir} - Chemins des scripts :
- Aide-mémoire du presse-papier :
{baseDir}/scripts/copy-to-clipboard.ts - Exportateur/extracteur de tweets :
{baseDir}/scripts/export-tweets.ts - Assistant API X officiel :
{baseDir}/scripts/x-api.ts
- Aide-mémoire du presse-papier :
- Résolvez le runtime
${BUN_X}: sibunest installé →bun; sinpxest disponible →npx -y bun; sinon, suggérez d'installer bun - Remplacez tous les
{baseDir}et${BUN_X}dans ce document par les valeurs réelles
Mode d'exécution
Cette compétence suit un modèle d'exécution hybride :
- API en premier (Recommandé pour les publications textuelles) : Essayez de publier le tweet en utilisant le script officiel de l'API X (
x-api.ts). C'est rapide, stable et n'utilise pas de ressources du navigateur. - Repli sur le navigateur : Si l'appel API échoue (par exemple, quota mensuel de 1500 tweets du niveau gratuit dépassé, limitation de débit) ou si les informations d'identification de l'API ne sont pas configurées dans
subscription.yaml, revenez automatiquement au mode d'utilisation de Chrome Computer Use / Pont CLI pour simuler les actions du navigateur.
Prérequis
- Pour le mode API : Configurez vos informations d'identification API X (jetons de contexte utilisateur OAuth 1.0a) dans votre fichier local
subscription.yaml(qui est git-ignoré) :x_api: api_key: "VOTRE_CLE_API" api_key_secret: "VOTRE_CLE_SECRETE_API" access_token: "VOTRE_JETON_ACCES" access_token_secret: "VOTRE_JETON_ACCES_SECRET" - Pour le repli sur le navigateur : Google Chrome installé, connecté à X (Twitter) dans Chrome, et autorisations d'accessibilité macOS accordées si nécessaire.
Flux de travail pour les publications régulières (texte et images)
Lors de l'exécution d'une publication :
- Démarrez le tour de l'agent en appelant
get_app_state(ou un outil équivalent) pourGoogle Chrome. - Ouvrez ou naviguez dans Google Chrome vers
https://x.com/compose/post. - Localisez la zone de saisie du compositeur de tweet.
- Saisissez le texte du message dans le compositeur à l'aide des entrées clavier du mode Computer Use.
- S'il y a des images à attacher (max 4) :
Pour chaque image :
a. Exécutez le script d'aide-mémoire du presse-papier pour copier l'image dans le presse-papier :
b. Collez l'image dans le compositeur en utilisant le raccourci de collage (${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts image /chemin/absolu/vers/image.pngsuper+vsur macOS,contrôle+vsur Windows/Linux). c. Attendez 2 à 3 secondes jusqu'à ce que X termine le téléchargement du média. - Sécurité de publication : Ne cliquez jamais sur
Publier,Posterou tout bouton équivalent pour publier le tweet sans obtenir une confirmation explicite et finale de l'utilisateur dans la conversation en cours. - Une fois que l'utilisateur confirme, cliquez sur le bouton
Posterpour publier. - Après la publication, fermez la fenêtre modale du compositeur pour que l'interface ne reste pas bloquée sur la boîte de dialogue de composition. Utilisez le bouton de fermeture ou la touche Échap :
- Sélecteur DOM :
[data-testid="app-bar-close"]ou[aria-label="Fermer"] - Repli : déclenchez un événement
keydownde la toucheÉchap
- Sélecteur DOM :
- Rechargement automatique du fil d'actualité (facultatif) : Si l'utilisateur a d'autres onglets ouverts vers son profil (par exemple,
x.com/[nom_utilisateur]) ou le fil d'accueil (x.com/home), rechargez-les pour que le nouveau tweet soit visible immédiatement.
var closeBtn = document.querySelector('[data-testid="app-bar-close"], [aria-label="Fermer"]');
if (closeBtn) { closeBtn.click(); }
else { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true })); }
Pont CLI (sans outils Computer Use)
Lorsque l'environnement ne dispose pas d'outils clavier/souris Computer Use, utilisez des méthodes spécifiques à la plateforme pour ouvrir Chrome et injecter du JavaScript dans la page.
macOS (AppleScript)
Ouvrir la page de composition :
open -a "Google Chrome" "https://x.com/compose/post"
Exécuter du JavaScript dans Chrome — écrivez d'abord le JS dans un fichier temporaire (évite les problèmes d'échappement de shell), puis exécutez-le via AppleScript :
cat > /tmp/tweet.js << 'EOF'
(function() { /* votre 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'
Motif : Écrivez toujours la charge utile JS dans un fichier temporaire d'abord. N'essayez PAS de faire un osascript -e en ligne avec du JS intégré — les conflits de guillemets/d'échappement provoqueront des erreurs d'analyse.
Fermez le compositeur après la publication (sinon la fenêtre modale reste ouverte) :
var closeBtn = document.querySelector('[data-testid="app-bar-close"], [aria-label="Fermer"]');
if (closeBtn) { closeBtn.click(); }
else { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true })); }
Recharger les onglets de profil/d'accueil pour refléter la nouvelle publication immédiatement (facultatif) :
osascript -e '
tell application "Google Chrome"
tell window 1
set tabList to every tab whose URL contains "x.com/hankunpeng" 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"
# Utilisez chrome-remote-interface ou un client CDP similaire pour exécuter du JS
Windows
Start-Process "chrome" "https://x.com/compose/post"
# Utilisez CDP via --remote-debugging-port ou l'automatisation PowerShell
Directives d'automatisation JavaScript / DOM (repli)
Si les événements clavier/souris directs de Computer Use ne sont pas disponibles, ou si vous fonctionnez en modes de script navigateur/CDP, suivez ces directives pour interagir avec l'éditeur React/Draft.js de X :
-
Détection de plusieurs éditeurs : Les pages X contiennent souvent plusieurs zones de texte de composition (par exemple, le compositeur en ligne sur le fil d'accueil et la boîte de dialogue modale active). Interrogez toujours tous les éditeurs :
var els = document.querySelectorAll('[data-testid="tweetTextarea_0"]'); var el = els.length > 1 ? els[els.length - 1] : els[0];Ciblez toujours le compositeur modal actif (généralement le dernier élément de la liste).
-
Liaison de la sélection et du focus : Avant d'insérer du texte, vous DEVEZ cliquer sur l'élément pour déclencher la liaison de sélection Draft.js, puis le focaliser :
el.click(); el.focus(); -
Préservation de la structure de l'éditeur :
- Ne PAS utiliser
el.innerHTML = ''oudocument.execCommand('delete')sur un compositeur vide. Effacer les nœuds DOM détruit la structure interne de wrapper de Draft.js (par exemple, le spanpublic-DraftStyleDefault-block), ce qui fait planter le composant React et désactive définitivement le bouton Publier. - Utilisez simplement
document.execCommand('insertText', false, texte)directement dans l'éditeur vide focalisé.
- Ne PAS utiliser
-
Déclenchement des mises à jour d'état React : Après l'insertion de texte, déclenchez un événement
inputavec bulles pour notifier React :el.dispatchEvent(new Event('input', { bubbles: true })); -
Localisation du bon bouton Publier : Les testids des boutons (
tweetButtonInlineettweetButton) peuvent être inversés selon le contexte. Recherchez toujours le bouton visible et activé :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();
Flux de travail de suppression de tweet
Lors de l'exécution d'une suppression :
- Ouvrez ou naviguez dans Google Chrome vers la page de profil de l'utilisateur (
https://x.com/[nom_utilisateur]) ou l'URL directe du tweet (https://x.com/[nom_utilisateur]/status/[idTweet]). - Recherchez le conteneur
<article>du tweet cible contenant le texte à supprimer. - Cliquez sur le bouton du menu d'options du tweet :
- Sélecteur DOM :
[data-testid="caret"]
- Sélecteur DOM :
- Attendez 1 à 2 secondes, puis cliquez sur l'élément de menu "Supprimer" :
- Sélecteur DOM : Un élément
[role="menuitem"]dont le texte contient "Supprimer" ou "删除".
- Sélecteur DOM : Un élément
- Attendez 1 à 2 secondes, puis cliquez sur le bouton de confirmation de suppression dans la feuille de dialogue :
- Sélecteur DOM :
[data-testid="confirmationSheetConfirm"](ou repli sur tout bouton de dialogue avec le texte "Supprimer" ou "删除").
- Sélecteur DOM :
Exemple de pont CLI (macOS)
Utilisez le même modèle de fichier temporaire + AppleScript que pour la publication. Remplacez TEXTE_TWEET_ICI par le contenu du tweet cible.
Étape 1 — Trouver le tweet et cliquer sur le caret :
cat > /tmp/del-1.js << 'EOF'
(function() {
var articles = document.querySelectorAll('article');
var target = null;
var needle = 'TEXTE_TWEET_ICI';
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 'ERREUR : tweet non trouvé';
var caret = target.querySelector('[data-testid="caret"]');
if (!caret) return 'ERREUR : caret non trouvé';
caret.click();
return 'OK : caret cliqué';
})();
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'
Étape 2 — Cliquer sur l'élément de menu Supprimer (attendez 1 à 2 secondes après l'étape 1) :
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 === 'Supprimer' || txt === '删除') {
// Cliquez sur l'élément menuitem lui-même, ou s'il contient un élément cliquable imbriqué
var clickTarget = items[i].querySelector('[role="menuitem"]') || items[i];
clickTarget.click();
return 'OK : supprimer cliqué (index ' + i + ')';
}
}
// Repli : cliquez sur le premier élément menuitem (toujours "Supprimer" dans le menu déroulant)
var first = document.querySelectorAll('[role="menuitem"]')[0];
if (first) { first.click(); return 'OK : premier menuitem cliqué'; }
return 'ERREUR : aucun menuitem';
})();
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'
Étape 3 — Confirmer la suppression (attendez 1 à 2 secondes après l'étape 2) :
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 === 'Supprimer' || txt === '删除') {
confirmBtn = buttons[i];
break;
}
}
}
if (!confirmBtn) return 'ERREUR : bouton de confirmation non trouvé';
confirmBtn.click();
return 'OK : confirmation cliquée';
})();
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'
Flux de travail d'exportation de tweets
Pour exporter/sauvegarder tous les tweets ou des tweets filtrés de votre page de profil :
- Exécutez le script d'exportation :
${BUN_X} {baseDir}/scripts/export-tweets.ts [dateDebut] [dateFin]- Filtres de date facultatifs : Vous pouvez passer
dateDebut(par exemple2026-06-01) etdateFin(par exemple2026-06-30) pour filtrer la sortie par plage de dates. S'ils sont omis, tous les tweets extraits sont exportés. - Fichier de sortie : Les tweets seront sauvegardés dans
/Users/alex/twitter/twitter.yamloù l'URL du tweet est la clé et le contenu textuel du tweet est la valeur.
- Filtres de date facultatifs : Vous pouvez passer