Communitygithub.com

Twitter スキル

Chrome Computer Use モードを使用して X(Twitter)にテキストや画像を投稿します。

Twitter スキル とは?

このスキルは、Google Antigravity エコシステム内で X(Twitter)への投稿を自動化するAIエージェント向けの複合実行スキルです。テキスト投稿には公式X API を使用し、API が利用できない場合やレート制限に達した場合は Chrome の Computer Use モードまたは CLI ブリッジによるブラウザ操作に自動的にフォールバックします。画像の添付(最大4枚)やツイートの削除、指定期間内のツイートエクスポート機能も備えており、macOS / Linux / Windows の主要プラットフォームに対応しています。本スキルを利用するには、X API 認証情報の設定、または Chrome ブラウザへのログインが必要です。

対応✓Claude Code~Codex CLI~Cursor✓Antigravity
npx skills add hankunpeng/hkp-skills

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Twitter スキル は何をしますか?

Chrome Computer Use モードを使って X(Twitter)にテキストと画像を投稿します。

スクリプトディレクトリ

重要: すべてのスクリプトはこのスキルの scripts/ サブディレクトリに配置されています。

エージェント実行手順:

  1. この SKILL.md ファイルのディレクトリパスを {baseDir} として特定する
  2. スクリプトパス:
    • クリップボードヘルパー: {baseDir}/scripts/copy-to-clipboard.ts
    • ツイートエクスポーター/スクレイパー: {baseDir}/scripts/export-tweets.ts
    • 公式X APIヘルパー: {baseDir}/scripts/x-api.ts
  3. ${BUN_X} ランタイムを解決: bun がインストールされていれば bun、npx が利用可能なら npx -y bun、どちらもなければ bun のインストールを促す
  4. このドキュメント内の {baseDir} と ${BUN_X} を実際の値に置き換える

実行モード

このスキルは ハイブリッド実行モデル に従います:

  1. API 優先(テキスト投稿に推奨): 公式X API スクリプト(x-api.ts)を使用してツイートを投稿します。高速で安定しており、ブラウザリソースを使用しません。
  2. ブラウザフォールバック: API 呼び出しが失敗した場合(例:月間1500ツイートのFree枠超過、レート制限)や subscription.yaml にAPI認証情報が設定されていない場合は、自動的に Chrome Computer Use モード / CLI ブリッジ にフォールバックしてブラウザ操作をシミュレートします。

前提条件

  • API モードの場合: ローカルの subscription.yaml ファイル(gitignore対象)に X API 認証情報(OAuth 1.0a ユーザーコンテキストトークン)を設定:
    x_api:
      api_key: "YOUR_API_KEY"
      api_key_secret: "YOUR_API_KEY_SECRET"
      access_token: "YOUR_ACCESS_TOKEN"
      access_token_secret: "YOUR_ACCESS_TOKEN_SECRET"
    
  • ブラウザフォールバックの場合: Google Chrome がインストールされ、Chrome で X(Twitter)にログイン済みで、必要に応じて macOS のアクセシビリティ権限が付与されていること。

通常投稿ワークフロー(テキスト&画像)

投稿を実行する場合:

  1. get_app_state(または同等のツール)を Google Chrome に対して呼び出してエージェントターンを開始する
  2. Google Chrome を開き、https://x.com/compose/post に移動する
  3. ツイート作成の入力ボックスを見つける
  4. Computer Use のキーボード入力を使って、作成ボックスに投稿テキストを入力する
  5. 添付する画像がある場合(最大4枚): 各画像に対して: a. クリップボードヘルパースクリプトを実行して画像をクリップボードにコピー:
    ${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts image /absolute/path/to/image.png
    
    b. ペーストショートカット(macOS は super+v、Windows/Linux は control+v)を使って画像を作成ボックスに貼り付ける c. X がメディアのアップロードを完了するまで2~3秒待つ
  6. 公開の安全性: 現在の会話でユーザーから明示的な最終確認を得るまでは、Publish、Post、またはそれに類するボタンを決してクリックしないでください。
  7. ユーザーが確認したら、Post ボタンをクリックして公開する
  8. 公開後、作成モーダルを閉じて UI が作成ダイアログに留まらないようにします。閉じるボタンまたは Escape キーを使用:
    • DOM セレクター: [data-testid="app-bar-close"] または [aria-label="Close"]
    • フォールバック: Escape キーダウンイベントを発火させる
  9. フィードの自動リロード(オプション): ユーザーがプロフィール(例:x.com/[username])やホームフィード(x.com/home)のタブを開いている場合、新しいツイートがすぐに表示されるようにそれらをリロードする。
var closeBtn = document.querySelector('[data-testid="app-bar-close"], [aria-label="Close"]');
if (closeBtn) { closeBtn.click(); }
else { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true })); }

CLI ブリッジ(Computer Use ツールなし)

環境に Computer Use のキーボード/マウスツールがない場合、プラットフォーム固有の方法で Chrome を開き、ページに JavaScript を注入します。

macOS(AppleScript)

作成ページを開く:

open -a "Google Chrome" "https://x.com/compose/post"

Chrome で JavaScript を実行 — まず JS を一時ファイルに書き込み(シェルエスケープの問題を回避)、その後 AppleScript で実行:

cat > /tmp/tweet.js << 'EOF'
(function() { /* コード */ })();
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'

パターン: 常に JS ペイロードを最初に一時ファイルに書き込んでください。インラインで osascript -e に JS を埋め込もうとしないでください。引用符/エスケープの競合によりパースエラーが発生します。

公開後に作成モーダルを閉じる(モーダルが開いたままにならないようにするため):

var closeBtn = document.querySelector('[data-testid="app-bar-close"], [aria-label="Close"]');
if (closeBtn) { closeBtn.click(); }
else { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true })); }

新しい投稿をすぐに反映させるためにプロフィール/ホームタブをリロード(オプション):

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"
# chrome-remote-interface または同様の CDP クライアントを使用して JS を実行

Windows

Start-Process "chrome" "https://x.com/compose/post"
# CDP を --remote-debugging-port または PowerShell 自動化経由で使用

JavaScript / DOM 自動化ガイドライン(フォールバック)

直接 Computer Use のキーボード/マウスイベントが利用できない場合、またはブラウザスクリプティング/CDP モードで実行している場合は、次のガイドラインに従って X の React/Draft.js エディターと対話してください:

  1. 複数エディターの検出: X のページにはしばしば複数の作成テキストエリア(例:ホームフィードのインライン作成とアクティブなモーダルダイアログ)が含まれています。常にすべてのエディターをクエリしてください:

    var els = document.querySelectorAll('[data-testid="tweetTextarea_0"]');
    var el = els.length > 1 ? els[els.length - 1] : els[0];
    

    常にアクティブなモーダル作成エディター(通常はリストの最後の要素)をターゲットにします。

  2. バインディング選択とフォーカス: テキストを挿入する前に、必ず要素をクリックして Draft.js の選択バインディングをトリガーし、フォーカスを当てます:

    el.click();
    el.focus();
    
  3. エディター構造の保持:

    • 空の作成ボックスに対して el.innerHTML = '' や document.execCommand('delete') を使用しないでください。DOM ノードを削除すると、Draft.js の内部ラッパー構造(例:public-DraftStyleDefault-block スパン)が破壊され、React コンポーネントがクラッシュし、Post ボタンが永久的に無効になります。
    • 空のフォーカスされたエディターには、単に document.execCommand('insertText', false, text) を直接使用してください。
  4. React 状態更新のトリガー: テキスト挿入後、バブリング input イベントを発火させて React に通知します:

    el.dispatchEvent(new Event('input', { bubbles: true }));
    
  5. 正しい Post ボタンの特定: ボタンの testid(tweetButtonInline と tweetButton)はコンテキストによって入れ替わる可能性があります。常に可視で有効なボタンをスキャンします:

    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();
    

ツイート削除ワークフロー

削除を実行する場合:

  1. Google Chrome を開き、ユーザーのプロフィールページ(https://x.com/[username])または直接ツイートの URL(https://x.com/[username]/status/[tweetId])に移動します。
  2. 削除したいテキストを含むターゲットツイートの <article> コンテナを検索します。
  3. ツイートのオプションメニューボタンをクリックします:
    • DOM セレクター: [data-testid="caret"]
  4. 1~2秒待ってから、「削除」メニュー項目をクリックします:
    • DOM セレクター: テキストに "Delete" または "删除" を含む [role="menuitem"] 要素
  5. 1~2秒待ってから、ダイアログシート内の確認削除ボタンをクリックします:
    • DOM セレクター: [data-testid="confirmationSheetConfirm"](またはテキストが "Delete" または "删除" のダイアログボタンにフォールバック)

CLI ブリッジ例(macOS)

投稿時と同じ一時ファイル + AppleScript パターンを使用します。TWEET_TEXT_HERE を対象ツイートの内容に置き換えてください。

ステップ 1 — ツイートを見つけてケアレットをクリック:

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'

ステップ 2 — 削除メニュー項目をクリック(ステップ1の後1~2秒待機):

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 === '删除') {
      // メニュー項目自体、またはその中に入れ子になったクリック可能な要素をクリック
      var clickTarget = items[i].querySelector('[role="menuitem"]') || items[i];
      clickTarget.click();
      return 'OK: delete clicked (index ' + i + ')';
    }
  }
  // フォールバック: 最初のメニュー項目をクリック(ドロップダウンでは常に "Delete")
  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'

ステップ 3 — 削除を確認(ステップ2の後1~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 === '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'

ツイートエクスポートワークフロー

プロフィールページからすべてのツイート(またはフィルタリングされたツイート)をエクスポート/保存するには:

  1. エクスポータースクリプトを実行:
    ${BUN_X} {baseDir}/scripts/export-tweets.ts [startDate] [endDate]
    
    • オプションの日付フィルター: startDate(例:2026-06-01)と endDate(例:2026-06-30)を渡して、出力を日付範囲でフィルタリングできます。省略した場合は、スクレイピングされたすべてのツイートがエクスポートされます。
    • 出力ファイル: ツイートは /Users/alex/twitter/twitter.yaml に保存され、ツイート URL がキー、ツイートのテキストコンテンツが値となります。

関連スキル