x-post
Publish a tweet or a multi-post thread (with images) to X/Twitter using the login that already exists in the user's local Chrome profile: no API key, no manual cookie copying. Dry-run first (compose +
Files2
View on GitHubSKILL.mdx-post/SKILL.md
GitHubx-post
把一段文字或一条线程(主帖 + 若干回复,可配图)发到 X。登录态直接取自用户本机 Chrome 已登录的 profile,脚本解密 cookie 注入独立的 agent-browser 会话,用 X 网页编辑器发出,最后回到帖子页核对段数与配图。默认只预览不发布,人审过截图后再加 --post。
前置检查(先做,缺一不可)
- macOS;
agent-browser、sqlite3、security、Node ≥ 20 在 PATH 中。 - 用户的 Chrome 某个 profile 已登录目标 X 账号。先跑
--check-login确认账号名,不要猜 profile:
node "$(dirname "$(readlink -f "$0")")/scripts/x_post.mjs" --check-login --profile Default
返回 {"loggedIn":true,"account":"<handle>"}。若报无登录 cookie,换 --profile "Profile 1" 等重试;仍无则让用户在 Chrome 里登录 x.com 后重来。
- 第一次读取钥匙串会弹 macOS 授权框,用户点"允许"即可;这是 Chrome Safe Storage 密码,脚本不落盘。
执行步骤
- 把内容写成文件(不要把长文本塞进命令行参数)。格式:帖子之间用单独一行
---分隔,第一段是主帖;image: 相对路径给该段配图,最多 4 张,只支持主帖配图。
主帖正文,可多行,可含 emoji
image: ./card.png
---
第一条回复
---
第二条回复
- 先预览:
node "<skill>/scripts/x_post.mjs" --file posts.md --profile Default --out ./x-post-out
脚本会:解密 cookie → 打开 x.com/home(带界面窗口)→ 校验已登录 → 填主帖、上传图片 → 每多一段点一次"+"(此时草稿会移入弹窗)→ 逐段回读文本、数附件、查发布按钮是否可用 → 截图到 x-post-out/dry-run.png → 输出 JSON。编辑器保持打开,用户可在窗口里看。
给用户看截图和四段原文,拿到明确同意。对外发布不可撤回,没有同意不要进入下一步。
发布:同一命令加
--post。脚本重新组稿(会先关闭上一步的窗口)、点击 Post/Post all、等弹窗关闭,再去个人主页找到新帖,打开帖子页核对:自己发的段数 = 输入段数,配图存在。输出:
{"mode":"posted","account":"...","url":"https://x.com/<handle>/status/<id>","verified":{"posts":4,"expectedPosts":4,"image":true,"expectedImage":true}}
- 记录链接:仓库若有
docs/launch-posts.md("已发布"表),把日期、渠道、链接、备注补进去;运营账本另记。
字数
X 按加权长度限 280:URL 记 23,中日韩字符和 emoji 记 2,其余记 1。中文纯文本上限约 140 字。脚本会在组稿前拒绝超限段落;Premium 账号可 --allow-long。写中文帖时按 140 字一段规划,线程比硬塞更好读。
已踩过的坑(脚本已处理,改脚本时别退回去)
- 点"+"后草稿进入
div[role="dialog"]弹窗,首页内联编辑器仍留在页面上:不限定弹窗作用域时tweetTextarea_0会命中两个元素报 strict mode violation,内联的 Post 按钮显示 disabled 也不代表弹窗里的 Post all 不可用。校验和点击都要以弹窗为根。 - 文本用
keyboard inserttext,不用type/fill:编辑器是 Draft.js,fill不触发状态更新,type对 emoji 和换行不稳。 agent-browser eval对对象只打印末行且是 JSON 编码后的字符串,脚本里用JSON.stringify包一层再解析两次。- Chrome 的 Cookies 数据库被 Chrome 锁着,必须复制一份再读;Chrome ≥ 130 解密后前 32 字节是 host key 的哈希,要剥掉。
- 解密后的 cookie 状态文件只存在临时目录,
finally里删除;agent-browser 会以--session-name持久化登录态,属于本机用户自己的数据,不要复制到仓库或日志。 - 发出后不要用首页判断成功,去个人主页按主帖前 20 字定位新帖,再打开帖子页数段数。
- zsh 下
rm /tmp/x-*.sqlite这种无匹配的通配会让整行命令中止;清理用脚本,不用 shell 通配。
边界
- 只做发布,不做定时、不做回复别人的帖子、不做删帖。需要回复某条帖子时另写流程。
- 图片只支持主帖;视频、投票、GIF 搜索不支持。
- 依赖 X 网页的
data-testid,改版即失效;失效时先跑--check-login与预览,看截图定位哪一步断了,再改选择器。