@youtyan/browser-pilot 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.ja.md +138 -0
- package/README.md +140 -0
- package/dist/cli.js +13919 -0
- package/extension/background.js +2332 -0
- package/extension/content.js +2107 -0
- package/extension/manifest.json +21 -0
- package/extension/popup.html +15 -0
- package/extension/popup.js +12 -0
- package/package.json +78 -0
- package/skills/bp-annotate-coords/SKILL.md +148 -0
- package/skills/bp-gemini-image/SKILL.md +107 -0
- package/skills/bp-generate-manual/SKILL.md +367 -0
- package/skills/bp-test-scripts/SKILL.md +296 -0
- package/skills/bp-testing/SKILL.md +193 -0
- package/skills/bp-usage/SKILL.md +219 -0
- package/skills/bp-x-operation/SKILL.md +545 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bp-x-operation
|
|
3
|
+
description: Use when operating X (Twitter) via browser-pilot MCP tools. Triggers on "X", "Twitter", "ツイート", "ポスト", "検索", "search X", "post to X", "Xで投稿", "Xで検索", "アカウント切り替え", "タイムライン".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# X (Twitter) 操作ガイド — Browser Pilot MCP
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Browser Pilot MCPツールでX (x.com) を操作する。検索・投稿(テキスト/画像/スレッド/下書き)・ツイート収集・アカウント切り替えをカバー。
|
|
11
|
+
|
|
12
|
+
## Pre-flight
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
browser_tab(action: "list") # Xタブを探す
|
|
16
|
+
browser_tab(action: "connect", tabId: <X_TAB_ID>)
|
|
17
|
+
browser_screenshot() # 現在のアカウント確認(左下に表示)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
左下のアカウント表示で現在のログインアカウントを必ず確認すること。
|
|
21
|
+
|
|
22
|
+
## アカウント切り替え
|
|
23
|
+
|
|
24
|
+
```dot
|
|
25
|
+
digraph switch {
|
|
26
|
+
rankdir=TB;
|
|
27
|
+
"screenshot で\n現在アカウント確認" [shape=box];
|
|
28
|
+
"目的のアカウント?" [shape=diamond];
|
|
29
|
+
"操作開始" [shape=doublecircle];
|
|
30
|
+
"AccountSwitcher\nクリック" [shape=box];
|
|
31
|
+
"目的アカウント\nクリック" [shape=box];
|
|
32
|
+
"切り替え完了\n確認" [shape=box];
|
|
33
|
+
|
|
34
|
+
"screenshot で\n現在アカウント確認" -> "目的のアカウント?";
|
|
35
|
+
"目的のアカウント?" -> "操作開始" [label="yes"];
|
|
36
|
+
"目的のアカウント?" -> "AccountSwitcher\nクリック" [label="no"];
|
|
37
|
+
"AccountSwitcher\nクリック" -> "目的アカウント\nクリック";
|
|
38
|
+
"目的アカウント\nクリック" -> "切り替え完了\n確認";
|
|
39
|
+
"切り替え完了\n確認" -> "操作開始";
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
# 1. アカウントスイッチャーを開く
|
|
45
|
+
browser_click(selector: '[data-testid="SideNav_AccountSwitcher_Button"]')
|
|
46
|
+
|
|
47
|
+
# 2. 目的のアカウントをクリック(テキストマッチ)
|
|
48
|
+
browser_click(text: "アカウント名")
|
|
49
|
+
|
|
50
|
+
# 3. 切り替え確認(screenshot で左下のアカウント名を確認)
|
|
51
|
+
browser_screenshot()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 検索
|
|
55
|
+
|
|
56
|
+
**重要: XのReact検索入力は `set_values` + Enter では発火しない。URL直接遷移で検索する。**
|
|
57
|
+
|
|
58
|
+
### 基本検索
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
# 1. 検索URL遷移(SPAリロードが発生する)
|
|
62
|
+
browser_get_page(url: "https://x.com/search?q=検索キーワード&src=typed_query")
|
|
63
|
+
# → 初回は "JavaScript is not available" エラーが返るが、ページ自体は正常にロードされる
|
|
64
|
+
|
|
65
|
+
# 2. 再接続(SPA再読み込み後、content scriptが再注入される)
|
|
66
|
+
browser_tab(action: "list")
|
|
67
|
+
browser_tab(action: "connect", tabId: <X_TAB_ID>)
|
|
68
|
+
|
|
69
|
+
# 3. 結果を取得
|
|
70
|
+
browser_get_page(mode: "markdown", maxLength: 5000)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**クエリのURLエンコード:** スペースは `%20`、日本語はそのままでOK(ブラウザが自動エンコード)。
|
|
74
|
+
|
|
75
|
+
### 検索結果タブ切り替え
|
|
76
|
+
|
|
77
|
+
検索結果ページには以下のタブがある:
|
|
78
|
+
- **Top** — 関連度順(デフォルト)
|
|
79
|
+
- **Latest** — 時系列順
|
|
80
|
+
- **People** — ユーザー検索
|
|
81
|
+
- **Media** — 画像・動画付き投稿
|
|
82
|
+
- **Lists** — リスト検索
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
browser_click(text: "Latest") # タブ切り替え
|
|
86
|
+
browser_wait_for(selector: '[data-testid="tweet"]', condition: "appear", timeout: 10000)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 検索のコツ
|
|
90
|
+
|
|
91
|
+
| テクニック | クエリ例 |
|
|
92
|
+
|-----------|---------|
|
|
93
|
+
| ユーザー指定 | `from:username キーワード` |
|
|
94
|
+
| 期間指定 | `キーワード since:2026-01-01 until:2026-03-01` |
|
|
95
|
+
| 画像付きのみ | `キーワード filter:images` |
|
|
96
|
+
| リンク付き | `キーワード filter:links` |
|
|
97
|
+
| リプライ除外 | `キーワード -filter:replies` |
|
|
98
|
+
| 最低いいね数 | `キーワード min_faves:100` |
|
|
99
|
+
| 最低RT数 | `キーワード min_retweets:10` |
|
|
100
|
+
| 完全一致 | `"完全一致フレーズ"` |
|
|
101
|
+
| OR検索 | `キーワードA OR キーワードB` |
|
|
102
|
+
| 除外 | `キーワード -除外ワード` |
|
|
103
|
+
|
|
104
|
+
## ツイート一覧取得
|
|
105
|
+
|
|
106
|
+
**`x_collect_tweets` MCPツールで1回の呼び出しでツイートを一括収集できる。** 内部でスクロール+パース+重複除外を自動実行。
|
|
107
|
+
|
|
108
|
+
### 基本使用
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
# 検索して収集(デフォルト: Latest タブ、100件、AD除外)
|
|
112
|
+
x_collect_tweets(query: "Claude Code", limit: 200)
|
|
113
|
+
|
|
114
|
+
# Top タブで検索
|
|
115
|
+
x_collect_tweets(query: "Claude Code", tab: "top", limit: 50)
|
|
116
|
+
|
|
117
|
+
# 現在接続中のページ(タイムライン等)から収集
|
|
118
|
+
x_collect_tweets(limit: 50)
|
|
119
|
+
|
|
120
|
+
# AD含む
|
|
121
|
+
x_collect_tweets(query: "AI", excludeAds: false)
|
|
122
|
+
|
|
123
|
+
# 日付範囲指定(X検索オペレータ since:/until: を自動付与)
|
|
124
|
+
x_collect_tweets(query: "Claude Code", since: "2026-01-01", until: "2026-03-01")
|
|
125
|
+
|
|
126
|
+
# 特定ユーザーの直近1ヶ月
|
|
127
|
+
x_collect_tweets(query: "from:username", since: "2026-02-07", until: "2026-03-07")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### パラメータ
|
|
131
|
+
|
|
132
|
+
| パラメータ | 型 | デフォルト | 説明 |
|
|
133
|
+
|-----------|-----|-----------|------|
|
|
134
|
+
| query | string | (省略) | 検索クエリ。省略時は現在のページから収集 |
|
|
135
|
+
| tab | "top" \| "latest" | "latest" | 検索タブ |
|
|
136
|
+
| limit | number | 100 | 取得上限 |
|
|
137
|
+
| excludeAds | boolean | true | AD除外 |
|
|
138
|
+
| since | string | (省略) | 開始日 (YYYY-MM-DD)。X検索オペレータとしてクエリに付与 |
|
|
139
|
+
| until | string | (省略) | 終了日 (YYYY-MM-DD)。X検索オペレータとしてクエリに付与 |
|
|
140
|
+
|
|
141
|
+
### 戻り値
|
|
142
|
+
|
|
143
|
+
JSON形式で `tweets` 配列と `meta` を返す:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"tweets": [
|
|
148
|
+
{
|
|
149
|
+
"handle": "@username",
|
|
150
|
+
"displayName": "表示名",
|
|
151
|
+
"text": "ツイート本文",
|
|
152
|
+
"time": "14h",
|
|
153
|
+
"url": "/username/status/123456",
|
|
154
|
+
"likes": "383",
|
|
155
|
+
"retweets": "135",
|
|
156
|
+
"replies": "244",
|
|
157
|
+
"views": "36K"
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"meta": {
|
|
161
|
+
"query": "Claude Code",
|
|
162
|
+
"total": 200,
|
|
163
|
+
"adsRemoved": 5,
|
|
164
|
+
"duplicatesRemoved": 30,
|
|
165
|
+
"scrollIterations": 15
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 注意事項
|
|
171
|
+
|
|
172
|
+
- Topタブは50-100件で枯渇しやすい。大量取得は **Latest** を使う
|
|
173
|
+
- 200件取得には数分かかる(スクロール+遅延読み込み待ち)
|
|
174
|
+
- 3回連続で新規ツイートが0件になると自動停止(枯渇検知)
|
|
175
|
+
- `query` 指定時はSPAリロード+タブ再接続を自動実行
|
|
176
|
+
|
|
177
|
+
### レートリミット対策(重要)
|
|
178
|
+
|
|
179
|
+
`x_collect_tweets` は内部で大量のスクロール+APIリクエストを送信する。**同一アカウントで連続実行するとレートリミットに引っかかる。**
|
|
180
|
+
|
|
181
|
+
**症状:**
|
|
182
|
+
- 検索ページに「問題が発生しました。再読み込みしてください。」が表示される
|
|
183
|
+
- `x_collect_tweets` が空配列(`tweets: []`)を返す
|
|
184
|
+
- `browser_get_page` での検索URL遷移後、再接続しても検索結果が表示されない
|
|
185
|
+
|
|
186
|
+
**リミットの仕組み:**
|
|
187
|
+
- レートリミットは**アカウント単位**。同じアカウントの別タブでも共有される
|
|
188
|
+
- リセット時間は**15〜60分**(動的。アカウント信頼度・Premium状態で変動)
|
|
189
|
+
- Web UIの検索も内部GraphQLエンドポイント経由のため、独自のレートリミットが存在する(公開APIとは別体系)
|
|
190
|
+
- `browser_get_page(url: "x.com/search?q=...")` による手動検索もカウントされる(同じGraphQLエンドポイントを使うため)
|
|
191
|
+
- スクロールによるツイート追加読み込みも1回ごとにリクエストが発生する
|
|
192
|
+
|
|
193
|
+
**リスクの目安(scrollIterations で判断):**
|
|
194
|
+
|
|
195
|
+
| scrollIterations合計 | リスク | 説明 |
|
|
196
|
+
|---------------------|--------|------|
|
|
197
|
+
| 〜30回 | 低 | 安全圏。limit 50×2回程度 |
|
|
198
|
+
| 30〜50回 | 中 | 注意。次のバッチでリミットに引っかかる可能性あり |
|
|
199
|
+
| 50回以上 | 高 | ほぼ確実にリミットに到達。別アカウントへ切り替えるべき |
|
|
200
|
+
|
|
201
|
+
**※ `meta.scrollIterations` を毎回確認し、累計を追跡すること。**
|
|
202
|
+
|
|
203
|
+
**大量収集タスクの事前計画:**
|
|
204
|
+
|
|
205
|
+
大量のツイートを収集するタスク(調査・リサーチ等)では、**開始前に以下を確認**:
|
|
206
|
+
|
|
207
|
+
1. `browser_tab(action: "list")` で、Xにログインしているタブが何アカウント分あるか確認
|
|
208
|
+
2. 使えるアカウント数に応じて、バッチ配分を事前に計画する
|
|
209
|
+
3. 1アカウントあたり `x_collect_tweets` は **2〜3回**(各50〜80件、scrollIterations合計30以下)を目安とする
|
|
210
|
+
4. クエリ設計の段階でOR検索を活用し、呼び出し回数を最小化する
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
# 悪い例: 4回の個別検索
|
|
214
|
+
x_collect_tweets(query: "Claude Code", limit: 100)
|
|
215
|
+
x_collect_tweets(query: "Cursor", limit: 100)
|
|
216
|
+
x_collect_tweets(query: "Copilot", limit: 100)
|
|
217
|
+
x_collect_tweets(query: "AI coding", limit: 100) # ← ここでリミット
|
|
218
|
+
|
|
219
|
+
# 良い例: OR検索で2回に集約
|
|
220
|
+
x_collect_tweets(query: 'Claude Code OR Cursor OR Copilot OR "AI coding"', limit: 100)
|
|
221
|
+
x_collect_tweets(query: 'GPT-5 OR OpenAI OR "AI agent" OR LLM', limit: 100)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**レートリミットの前兆検知:**
|
|
225
|
+
|
|
226
|
+
以下の兆候が出たら、次のバッチは**別アカウントに切り替える**か**15分以上待つ**:
|
|
227
|
+
|
|
228
|
+
- `x_collect_tweets` の戻り値で `scrollIterations` が急に増えている(同じlimitなのに前回より多くスクロールしている)
|
|
229
|
+
- 取得件数が想定より大幅に少ない(limit: 100 に対して 20件しか取れない等)
|
|
230
|
+
- `duplicatesRemoved` が異常に多い(重複が増える=新規ツイートの読み込みが遅くなっている兆候)
|
|
231
|
+
|
|
232
|
+
**レートリミット回避戦略:**
|
|
233
|
+
1. **アカウント分散**: 複数アカウントでログイン中のXタブがある場合、**別アカウントのタブに切り替えて検索を継続**できる(レートリミットはアカウント単位のため)
|
|
234
|
+
2. **バッチ間隔**: 2〜3回の `x_collect_tweets` を実行したら、**最低15分**待ってから再開
|
|
235
|
+
3. **クエリ最適化**: OR検索で複数キーワードを1クエリにまとめ、呼び出し回数を減らす
|
|
236
|
+
4. **limit を抑える**: 50〜80件に抑えればスクロール回数が減り、リミットに引っかかりにくい
|
|
237
|
+
5. **収集後にホームに戻る**: 検索ページに留まるとバックグラウンドでリクエストが発生し続ける場合がある。収集完了後は `browser_click(selector: '[data-testid="AppTabBar_Home_Link"]')` でホームに戻る
|
|
238
|
+
|
|
239
|
+
**レートリミットに引っかかった場合:**
|
|
240
|
+
1. そのアカウントでの検索は一旦停止
|
|
241
|
+
2. 別アカウントのXタブがあれば、そちらに `browser_tab(action: "connect")` で切り替えて継続
|
|
242
|
+
3. 別アカウントもなければ15〜60分待つ(15分で復旧しなければ30分、それでもダメなら60分)
|
|
243
|
+
4. **「やりなおす」ボタンをクリックしても復旧しない**(リミットが解除されるまで無意味)
|
|
244
|
+
5. 待機中に他の作業(収集済みデータの整理・分析等)を進める
|
|
245
|
+
|
|
246
|
+
## 投稿
|
|
247
|
+
|
|
248
|
+
**投稿前チェック(必須):**
|
|
249
|
+
|
|
250
|
+
投稿前に `browser_screenshot()` で現在のログインアカウントを確認すること。
|
|
251
|
+
|
|
252
|
+
### テキスト投稿(単一行・複数行共通)
|
|
253
|
+
|
|
254
|
+
`browser_set_values` は単一行・複数行どちらも対応。改行付きテキストも直接入力可能(内部で ClipboardEvent paste を使用)。
|
|
255
|
+
|
|
256
|
+
**重要: `browser_tab(action: "create", url: "compose/post")` は使うな。** 新タブではホーム画面のインライン入力欄とモーダルの入力欄が両方存在し、`tweetTextarea_0` が背景側を先にマッチする。必ず既存Xタブで `SideNav_NewTweet_Button` をクリックしてモーダルを開くこと。
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
# 0. アカウント確認
|
|
260
|
+
browser_screenshot()
|
|
261
|
+
|
|
262
|
+
# 1. 既存Xタブでモーダルを開く(新タブを作らない)
|
|
263
|
+
browser_click(selector: '[data-testid="SideNav_NewTweet_Button"]')
|
|
264
|
+
browser_wait_for(selector: '[aria-modal="true"]', condition: "appear", timeout: 5000)
|
|
265
|
+
|
|
266
|
+
# 2. モーダル内のテキスト欄に入力([aria-modal="true"] で限定必須)
|
|
267
|
+
browser_set_values(selector: '[aria-modal="true"] div[contenteditable="true"]', value: "1行目\n2行目\n3行目")
|
|
268
|
+
|
|
269
|
+
# 3. 投稿
|
|
270
|
+
browser_click(selector: '[data-testid="tweetButton"]')
|
|
271
|
+
|
|
272
|
+
# 4. 投稿完了確認
|
|
273
|
+
browser_wait_for(selector: '[data-testid="toast"]', condition: "appear", timeout: 10000)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 画像付き投稿
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
# 1. テキスト入力(上記の手順1-2)
|
|
280
|
+
|
|
281
|
+
# 2. 画像を貼り付け(モーダル内を指定。複数枚は paste_file を繰り返す、最大4枚)
|
|
282
|
+
browser_paste_file(filePath: "/path/to/image.png", selector: '[aria-modal="true"] [data-testid="tweetTextarea_0"]')
|
|
283
|
+
browser_wait_for(selector: '[data-testid="attachments"]', condition: "appear", timeout: 10000)
|
|
284
|
+
|
|
285
|
+
# 3. 投稿
|
|
286
|
+
browser_click(selector: '[data-testid="tweetButton"]')
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### スレッド投稿
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
# 1. 1つ目のツイートを入力(上記の手順1-2)
|
|
293
|
+
|
|
294
|
+
# 2. スレッド追加ボタンをクリック
|
|
295
|
+
browser_click(selector: '[data-testid="addButton"]')
|
|
296
|
+
browser_wait_for(selector: '[data-testid="tweetTextarea_1"]', condition: "appear")
|
|
297
|
+
|
|
298
|
+
# 3. 2つ目のツイートを入力
|
|
299
|
+
browser_set_values(selector: 'div[data-testid="tweetTextarea_1"] div[contenteditable="true"]', value: "2つ目のツイート")
|
|
300
|
+
|
|
301
|
+
# 4. さらに追加する場合は addButton → tweetTextarea_N を繰り返す
|
|
302
|
+
|
|
303
|
+
# 5. 全ツイート投稿
|
|
304
|
+
browser_click(selector: '[data-testid="tweetButton"]')
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### 下書き保存・読込
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
# 保存: テキスト入力後、モーダルを閉じる → 下書き確認ダイアログで保存
|
|
311
|
+
browser_click(selector: '[aria-label="Close"]')
|
|
312
|
+
browser_click(selector: '[data-testid="confirmationSheetConfirm"]') # "Save"
|
|
313
|
+
|
|
314
|
+
# 読込: Drafts から復元
|
|
315
|
+
browser_tab(action: "create", url: "https://x.com/compose/post")
|
|
316
|
+
browser_click(text: "Drafts")
|
|
317
|
+
browser_wait_for(selector: '[data-testid="tweet"]', condition: "appear")
|
|
318
|
+
# 下書きをクリックして編集再開
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## リポスト(RT)
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
# 1. リポストボタンをクリック(ツイート内の retweet ボタン)
|
|
325
|
+
browser_click(selector: '[data-testid="retweet"]')
|
|
326
|
+
|
|
327
|
+
# 2. メニューから「Repost」を選択(引用RTではなく単純リポスト)
|
|
328
|
+
browser_click(selector: '[data-testid="retweetConfirm"]')
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**リポスト解除:**
|
|
332
|
+
```
|
|
333
|
+
# 1. 緑色になっているリポストボタンをクリック
|
|
334
|
+
browser_click(selector: '[data-testid="unretweet"]')
|
|
335
|
+
|
|
336
|
+
# 2. 確認ダイアログで解除
|
|
337
|
+
browser_click(selector: '[data-testid="unretweetConfirm"]')
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**注意:**
|
|
341
|
+
- リポストボタンクリック後にメニューが出る(Repost / Quote)。単純RTは `retweetConfirm`
|
|
342
|
+
- 既にリポスト済みの場合、ボタンの `data-testid` は `unretweet` に変わる
|
|
343
|
+
|
|
344
|
+
## リプライ(返信)
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
# 1. 対象ツイートのリプライボタンをクリック
|
|
348
|
+
browser_click(selector: '[data-testid="reply"]')
|
|
349
|
+
browser_wait_for(selector: '[aria-modal="true"]', condition: "appear", timeout: 5000)
|
|
350
|
+
|
|
351
|
+
# 2. リプライモーダル内のテキスト欄に入力
|
|
352
|
+
browser_set_values(selector: '[aria-modal="true"] div[contenteditable="true"]', value: "リプライ本文")
|
|
353
|
+
|
|
354
|
+
# 3. 送信
|
|
355
|
+
browser_click(selector: '[data-testid="tweetButton"]')
|
|
356
|
+
|
|
357
|
+
# 4. 完了確認
|
|
358
|
+
browser_wait_for(selector: '[data-testid="toast"]', condition: "appear", timeout: 10000)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**注意:**
|
|
362
|
+
- リプライボタンクリック後、通常投稿と同じモーダルが開く。セレクタも同じ(`tweetButton`)
|
|
363
|
+
- ツイート詳細ページでは、インラインのリプライ入力欄が表示される場合がある。モーダルが開かない場合は `[data-testid="tweetTextarea_0"] div[contenteditable="true"]` に直接入力し、`[data-testid="tweetButtonInline"]` で送信
|
|
364
|
+
- 画像付きリプライも可能(通常の画像付き投稿と同じ手順で `browser_paste_file` を使う)
|
|
365
|
+
|
|
366
|
+
## 引用RT(Quote)
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
# 1. リポストボタンをクリック(メニューが出る)
|
|
370
|
+
browser_click(selector: '[data-testid="retweet"]')
|
|
371
|
+
|
|
372
|
+
# 2. メニューから「Quote」を選択(retweetConfirm ではなく Quote を選ぶ)
|
|
373
|
+
browser_click(text: "Quote")
|
|
374
|
+
browser_wait_for(selector: '[aria-modal="true"]', condition: "appear", timeout: 5000)
|
|
375
|
+
|
|
376
|
+
# 3. 引用モーダル内のテキスト欄に入力
|
|
377
|
+
browser_set_values(selector: '[aria-modal="true"] div[contenteditable="true"]', value: "引用コメント")
|
|
378
|
+
|
|
379
|
+
# 4. 投稿
|
|
380
|
+
browser_click(selector: '[data-testid="tweetButton"]')
|
|
381
|
+
|
|
382
|
+
# 5. 完了確認
|
|
383
|
+
browser_wait_for(selector: '[data-testid="toast"]', condition: "appear", timeout: 10000)
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**注意:**
|
|
387
|
+
- リポストメニューには「Repost」と「Quote」の2つが表示される。引用RTは「Quote」を選ぶ
|
|
388
|
+
- 引用モーダルには元ツイートのプレビューが表示される。テキスト入力欄は通常投稿と同じセレクタ
|
|
389
|
+
- 画像付き引用RTも可能(`browser_paste_file` で添付)
|
|
390
|
+
- 「Quote」のテキストマッチで見つからない場合は `[role="menuitem"]:nth-child(2)` を試す
|
|
391
|
+
|
|
392
|
+
## ツイート削除
|
|
393
|
+
|
|
394
|
+
```
|
|
395
|
+
# 1. ツイート詳細ページに遷移
|
|
396
|
+
browser_get_page(url: "https://x.com/{USERNAME}/status/{TWEET_ID}")
|
|
397
|
+
# → 再接続: browser_tab(action: "list") → browser_tab(action: "connect")
|
|
398
|
+
|
|
399
|
+
# 2. メニューを開く(ツイート右上の三点リーダー)
|
|
400
|
+
browser_click(selector: '[data-testid="caret"]')
|
|
401
|
+
|
|
402
|
+
# 3. Delete を選択(メニューの最初の項目)
|
|
403
|
+
browser_click(selector: '[data-testid="Dropdown"] [role="menuitem"]:first-child')
|
|
404
|
+
|
|
405
|
+
# 4. 確認ダイアログで Delete を確定
|
|
406
|
+
browser_click(selector: '[data-testid="confirmationSheetConfirm"]')
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**注意:** 自分のツイートのみ削除可能。他ユーザーのツイートにはDeleteメニューが出ない。
|
|
410
|
+
|
|
411
|
+
## ユーザープロフィール遷移
|
|
412
|
+
|
|
413
|
+
ユーザーのプロフィールページに遷移する方法は2つある。**方法Aを推奨。**
|
|
414
|
+
|
|
415
|
+
### 方法A: TL・検索結果からユーザー名クリック(推奨)
|
|
416
|
+
|
|
417
|
+
タイムラインや検索結果に表示されているユーザー名(`@username` 部分)やアバターをクリックする。
|
|
418
|
+
|
|
419
|
+
```
|
|
420
|
+
# ユーザー名のテキストをクリック(部分一致)
|
|
421
|
+
browser_click(text: "@username")
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**これはSPA内遷移のため再接続不要。** そのままフォローボタン等を操作できる。
|
|
425
|
+
|
|
426
|
+
### 方法B: URL直接遷移
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
# 1. URL遷移(SPAリロードが発生する)
|
|
430
|
+
browser_get_page(url: "https://x.com/username")
|
|
431
|
+
# → 初回は "JavaScript is not available" エラーが返る
|
|
432
|
+
|
|
433
|
+
# 2. 再接続(必須)
|
|
434
|
+
browser_tab(action: "list")
|
|
435
|
+
browser_tab(action: "connect", tabId: <X_TAB_ID>)
|
|
436
|
+
|
|
437
|
+
# 3. 表示確認
|
|
438
|
+
browser_screenshot()
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**注意:**
|
|
442
|
+
- `browser_get_page(url: ...)` でのURL遷移はSPAリロードを引き起こすため、**毎回再接続が必要**
|
|
443
|
+
- 再接続を忘れると操作が一切できない(404やエラーに見える)
|
|
444
|
+
- 方法Aが使えない場合(ユーザー名だけ分かっている場合)のみ方法Bを使う
|
|
445
|
+
|
|
446
|
+
## フォロー / フォロー解除
|
|
447
|
+
|
|
448
|
+
**重要: フォローボタンの `data-testid` は `{userId}-follow` の形式で動的。固定セレクタではない。`aria-label` を使うこと。**
|
|
449
|
+
|
|
450
|
+
### フォロー
|
|
451
|
+
|
|
452
|
+
```
|
|
453
|
+
# プロフィールページで(遷移方法は上記参照)
|
|
454
|
+
browser_click(selector: '[aria-label="Follow @username"]')
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
`@username` は対象のハンドル名に置き換える。
|
|
458
|
+
|
|
459
|
+
### フォロー解除
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
browser_click(selector: '[aria-label="Following @username"]')
|
|
463
|
+
# → 確認ダイアログが出る
|
|
464
|
+
browser_click(selector: '[data-testid="confirmationSheetConfirm"]')
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### いいね
|
|
468
|
+
|
|
469
|
+
```
|
|
470
|
+
# ツイート内のいいねボタンをクリック
|
|
471
|
+
browser_click(selector: '[data-testid="like"]')
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
いいね済みの場合は `data-testid="unlike"` に変わる。
|
|
475
|
+
|
|
476
|
+
**注意:**
|
|
477
|
+
- サイドバーの「Who to follow」にも同じ `aria-label="Follow @..."` のボタンがあるが、プロフィールページのメインFollowボタンが先にマッチする
|
|
478
|
+
- フォロー成功時はボタンが「Following」に変わる。`browser_screenshot()` で確認可能
|
|
479
|
+
- フォロー解除の確認ダイアログが出ない場合もある(X側のUI変更による)
|
|
480
|
+
|
|
481
|
+
## Quick Reference — data-testid 一覧
|
|
482
|
+
|
|
483
|
+
| 要素 | data-testid |
|
|
484
|
+
|------|-------------|
|
|
485
|
+
| ホーム | `AppTabBar_Home_Link` |
|
|
486
|
+
| Explore(検索) | `AppTabBar_Explore_Link` |
|
|
487
|
+
| 通知 | `AppTabBar_Notifications_Link` |
|
|
488
|
+
| プロフィール | `AppTabBar_Profile_Link` |
|
|
489
|
+
| DM | `AppTabBar_DirectMessage_Link` |
|
|
490
|
+
| 検索入力 | `SearchBox_Search_Input` |
|
|
491
|
+
| 投稿テキスト欄 | `tweetTextarea_0` |
|
|
492
|
+
| 投稿ボタン(インライン) | `tweetButtonInline` |
|
|
493
|
+
| 投稿ボタン(モーダル) | `tweetButton` |
|
|
494
|
+
| Post(サイドバー) | `SideNav_NewTweet_Button` |
|
|
495
|
+
| アカウント切替 | `SideNav_AccountSwitcher_Button` |
|
|
496
|
+
| ツイート要素 | `tweet` |
|
|
497
|
+
| トースト通知 | `toast` |
|
|
498
|
+
| いいね | `like` |
|
|
499
|
+
| リポスト(RT) | `retweet` |
|
|
500
|
+
| リポスト確定 | `retweetConfirm` |
|
|
501
|
+
| リポスト解除 | `unretweet` |
|
|
502
|
+
| リポスト解除確定 | `unretweetConfirm` |
|
|
503
|
+
| リプライ | `reply` |
|
|
504
|
+
| 引用RT(Quoteメニュー) | テキスト `"Quote"` or `[role="menuitem"]:nth-child(2)` |
|
|
505
|
+
| フォロー | `aria-label="Follow @username"` (動的testid) |
|
|
506
|
+
| フォロー中 | `aria-label="Following @username"` |
|
|
507
|
+
| ブックマーク | `bookmark` |
|
|
508
|
+
| スレッド追加 | `addButton` |
|
|
509
|
+
| 添付ファイル表示 | `attachments` |
|
|
510
|
+
| ファイル入力 | `fileInput` |
|
|
511
|
+
| GIF | `gifSearchButton` |
|
|
512
|
+
| 投票 | `createPollButton` |
|
|
513
|
+
| Moreメニュー | `AppTabBar_More_Menu` |
|
|
514
|
+
| ツイートメニュー(三点) | `caret` |
|
|
515
|
+
| ドロップダウンメニュー | `Dropdown` |
|
|
516
|
+
| 確認ダイアログ確定 | `confirmationSheetConfirm` |
|
|
517
|
+
| 確認ダイアログキャンセル | `confirmationSheetCancel` |
|
|
518
|
+
|
|
519
|
+
## Common Mistakes
|
|
520
|
+
|
|
521
|
+
| 間違い | 正しい対応 |
|
|
522
|
+
|--------|-----------|
|
|
523
|
+
| 意図しないアカウントで投稿する | **投稿前に必ずscreenshotでアカウント確認。** 違うアカウントなら切り替え |
|
|
524
|
+
| 検索で`set_values`+Enterを使う | **XのReact検索入力はsynthetic eventsで発火しない。** URL直接遷移(`browser_get_page(url:...)`)を使う |
|
|
525
|
+
| 検索URL遷移後にエラーが返る | SPAリロードで初回は"JS not available"が返る。`browser_tab(list)` → `connect` で再接続すれば正常 |
|
|
526
|
+
| 投稿テキスト欄が見つからない | Homeに戻る。Explore等では投稿欄がない |
|
|
527
|
+
| 連続投稿する | X側のレート制限に注意。短時間に大量投稿しない |
|
|
528
|
+
| アカウント切替後に確認しない | 必ず `browser_screenshot()` で切り替え完了を確認 |
|
|
529
|
+
| 投稿後に完了確認しない | `browser_wait_for(selector: '[data-testid="toast"]')` でトースト確認 |
|
|
530
|
+
| モーダルとインラインの投稿ボタンを混同 | インライン: `tweetButtonInline`、モーダル: `tweetButton` |
|
|
531
|
+
| 複数行テキストを`set_values`でinput/textareaに入力 | contenteditable(`div[contenteditable="true"]`)に対して `set_values` を使う。input/textarea ではなくcontenteditable divがXの入力欄 |
|
|
532
|
+
| 削除で"Delete"テキスト検索を使う | テキストマッチでは見つからない。`[data-testid="Dropdown"] [role="menuitem"]:first-child` を使う |
|
|
533
|
+
| `browser_tab(create, url: "compose/post")` で投稿する | **新タブではホームのインライン入力欄とモーダルが共存し、背景側が先にマッチする。** 既存Xタブで `SideNav_NewTweet_Button` クリック → `[aria-modal="true"]` 内セレクタを使う |
|
|
534
|
+
| `x_collect_tweets` を同一アカウントで4回以上連続実行 | **レートリミットに引っかかる。** 1アカウント3回まで。それ以上は別アカウントに切り替えるか15分以上待つ |
|
|
535
|
+
| レートリミット後に「やりなおす」ボタンをクリック | **リミット解除まで無意味。** 別アカウントのタブに切り替えるか待つ |
|
|
536
|
+
| レートリミットをSPAバグと誤認する | **検索で「問題が発生しました」=レートリミット。** 別アカウントで検索が動くなら確定 |
|
|
537
|
+
| `browser_get_page(url: "x.com/username")` で遷移後にそのまま操作する | **SPAリロード後は再接続必須。** `browser_tab(list)` → `connect` してから操作。TLからのユーザー名クリック(SPA内遷移)なら再接続不要 |
|
|
538
|
+
| フォローボタンを `data-testid="follow"` で探す | **フォローボタンのtestidは `{userId}-follow` で動的。** `aria-label="Follow @username"` を使う |
|
|
539
|
+
|
|
540
|
+
## 安全ルール
|
|
541
|
+
|
|
542
|
+
1. **投稿前に必ずアカウント確認。** screenshotで左下のアカウント名をチェック
|
|
543
|
+
2. **連投禁止。** 短時間に複数投稿しない。最低でも数分間隔を空ける
|
|
544
|
+
3. **不適切な内容を投稿しない。** 公序良俗に反する内容、スパム、誹謗中傷は禁止
|
|
545
|
+
4. **投稿内容はユーザーの明示的な指示に基づくこと。** 勝手に投稿しない
|