Communitygithub.com

Compétence Twitter

Publie du texte et des images sur X (Twitter) via le mode d'utilisation de Chrome Computer Use.

Qu'est-ce que Compétence Twitter ?

Cette compétence permet à un agent AI de publier et supprimer des tweets, ainsi que d'exporter des tweets depuis X (Twitter) en utilisant un modèle hybride : d'abord via l'API officielle de X (recommandé pour les publications textuelles), avec un repli automatique vers l'automatisation du navigateur via Chrome Computer Use Mode. Elle prend en charge les images, la gestion des erreurs, et fournit des workflows détaillés pour la publication, la suppression et l'exportation. Fonctionne sur macOS, Linux et Windows, en utilisant soit les outils de Computer Use, soit un pont CLI via AppleScript (macOS) ou CDP. Utile pour les développeurs qui intègrent des agents AI avec des plateformes sociales, en respectant les limitations d'API et en garantissant une confirmation humaine avant toute publication.

Compatible avec✓Claude Code~Codex CLI~Cursor✓Antigravity
npx skills add hankunpeng/hkp-skills

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

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 :

  1. Déterminez le chemin du répertoire de ce fichier SKILL.md comme {baseDir}
  2. 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
  3. Résolvez le runtime ${BUN_X} : si bun est installé → bun ; si npx est disponible → npx -y bun ; sinon, suggérez d'installer bun
  4. 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 :

  1. 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.
  2. 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 :

  1. Démarrez le tour de l'agent en appelant get_app_state (ou un outil équivalent) pour Google Chrome.
  2. Ouvrez ou naviguez dans Google Chrome vers https://x.com/compose/post.
  3. Localisez la zone de saisie du compositeur de tweet.
  4. Saisissez le texte du message dans le compositeur à l'aide des entrées clavier du mode Computer Use.
  5. 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 :
    ${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts image /chemin/absolu/vers/image.png
    
    b. Collez l'image dans le compositeur en utilisant le raccourci de collage (super+v sur macOS, contrôle+v sur Windows/Linux). c. Attendez 2 à 3 secondes jusqu'à ce que X termine le téléchargement du média.
  6. Sécurité de publication : Ne cliquez jamais sur Publier, Poster ou tout bouton équivalent pour publier le tweet sans obtenir une confirmation explicite et finale de l'utilisateur dans la conversation en cours.
  7. Une fois que l'utilisateur confirme, cliquez sur le bouton Poster pour publier.
  8. 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 keydown de la touche Échap
  9. 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 :

  1. 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).

  2. 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();
    
  3. Préservation de la structure de l'éditeur :

    • Ne PAS utiliser el.innerHTML = '' ou document.execCommand('delete') sur un compositeur vide. Effacer les nœuds DOM détruit la structure interne de wrapper de Draft.js (par exemple, le span public-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é.
  4. Déclenchement des mises à jour d'état React : Après l'insertion de texte, déclenchez un événement input avec bulles pour notifier React :

    el.dispatchEvent(new Event('input', { bubbles: true }));
    
  5. Localisation du bon bouton Publier : Les testids des boutons (tweetButtonInline et tweetButton) 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 :

  1. 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]).
  2. Recherchez le conteneur <article> du tweet cible contenant le texte à supprimer.
  3. Cliquez sur le bouton du menu d'options du tweet :
    • Sélecteur DOM : [data-testid="caret"]
  4. 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 "删除".
  5. 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 "删除").

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 :

  1. Exécutez le script d'exportation :
    ${BUN_X} {baseDir}/scripts/export-tweets.ts [dateDebut] [dateFin]
    
    • Filtres de date facultatifs : Vous pouvez passer dateDebut (par exemple 2026-06-01) et dateFin (par exemple 2026-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.yaml où l'URL du tweet est la clé et le contenu textuel du tweet est la valeur.

Skills associés