重开仓库内容:改推五个技能(browser-harness / humanizer / humanizer-zh / product-planning / session-mechanism)
按授权清空原有内容后重新提交(原 oil-ui-pro 一并移出,可从历史恢复)。 browser-harness 剔除 .venv 等运行环境;根 .gitignore 补记 .venv/ 与 node_modules/。
This commit is contained in:
1 parent
9a152a1952
commit
777f7fe5d0
240 files changed
+52518
-4409
No files matched your search
@@ -0,0 +1,67 @@
|
||||
# X (Twitter) — Posting & Auth
|
||||
|
||||
## Login
|
||||
|
||||
Navigate to `https://x.com/i/flow/login` (not `https://x.com/`) to get the full sign-in modal. The homepage may render a truncated version of the auth buttons.
|
||||
|
||||
### Google sign-in (FedCM) — cannot be automated
|
||||
|
||||
The "Sign in as <user>" Google button uses Chrome's Federated Credential Management (FedCM) API. CDP mouse events (`Input.dispatchMouseEvent`) do **not** count as a trusted user gesture for FedCM, and the button lives in a sandboxed cross-origin iframe (`accounts.google.com`). Clicking it via automation has no effect.
|
||||
|
||||
**If the user wants Google sign-in, ask them to click the button themselves**, then wait for confirmation before proceeding.
|
||||
|
||||
### Username / password flow
|
||||
|
||||
Use the standard form on `https://x.com/i/flow/login`. Ask the user for credentials — do not guess or read them from screenshots.
|
||||
|
||||
## Composing and posting a tweet
|
||||
|
||||
Once logged in, the home feed is at `https://x.com/home`.
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
# Find the compose box and type
|
||||
result = js(r'''
|
||||
var el = document.querySelector("[data-testid=\"tweetTextarea_0\"]");
|
||||
if (!el) return null;
|
||||
var r = el.getBoundingClientRect();
|
||||
return JSON.stringify({x: Math.round(r.x + r.width/2), y: Math.round(r.y + r.height/2)});
|
||||
''')
|
||||
if result is None:
|
||||
raise RuntimeError("compose textarea not found — are you logged in and on x.com/home?")
|
||||
pos = json.loads(result)
|
||||
click_at_xy(pos["x"], pos["y"])
|
||||
type_text("hello world!")
|
||||
|
||||
# Find and click Post
|
||||
btn = js(r'''
|
||||
var b = document.querySelector("[data-testid=\"tweetButtonInline\"]")
|
||||
|| document.querySelector("[data-testid=\"tweetButton\"]");
|
||||
if (!b) return null;
|
||||
var r = b.getBoundingClientRect();
|
||||
return JSON.stringify({x: Math.round(r.x + r.width/2), y: Math.round(r.y + r.height/2)});
|
||||
''')
|
||||
if btn is None:
|
||||
raise RuntimeError("post button not found — did the compose surface fail to open?")
|
||||
pos = json.loads(btn)
|
||||
click_at_xy(pos["x"], pos["y"])
|
||||
```
|
||||
|
||||
### Stable selectors
|
||||
|
||||
| Element | Selector |
|
||||
|---|---|
|
||||
| Compose textbox | `[data-testid="tweetTextarea_0"]` |
|
||||
| Post button (home feed inline) | `[data-testid="tweetButtonInline"]` |
|
||||
| Post button (modal) | `[data-testid="tweetButton"]` |
|
||||
|
||||
### Confirmation
|
||||
|
||||
A toast "Your post was sent." appears at the bottom of the page after a successful post. Verify with a screenshot.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- The home feed compose area and the sidebar "Post" button both open a compose surface, but `tweetButtonInline` is specific to the inline feed composer. If you open a modal (e.g. via the sidebar "Post" button), use `tweetButton` instead.
|
||||
- If the Grok side panel opens unexpectedly, click a neutral area (e.g. left sidebar) to dismiss it before interacting with the compose box.
|
||||
- `twitter.com` redirects to `x.com` — all canonical URLs use `x.com`.
|
||||
Reference in new issue
Block a user