x-comment —— X 时间线自动评论
本 skill 固化的是我们在 profile=openclaw 环境下实测的一套流程:
- 浏览 X 首页推荐时间线(Home timeline)
- 筛选技术类、BTC 相关的推文
- 自动完成:打开 reply + 模拟真人慢速打字填入评论 + 直接发送
核心原则:全自动完成互动,输入评论后直接发送,不询问用户确认。只评论不点赞。
0. 环境约束与边界
浏览器与 profile
- 使用托管浏览器
profile="openclaw",通过 HTTP 代理访问 X;
- 需要保证该浏览器 profile 内已经登录目标 X 账号,否则不会出现 like/reply 按钮。
交互能力现状
- 已实测可行:
- 能在搜索结果页/详情页获取完整 DOM(含
data-testid="like"/"reply")。
- 能点击 like / reply 按钮。
- 能用
browser act + type + slowly: true 模拟真人键盘输入,让 Reply 按钮亮起。
- 能自动点击发送按钮完成评论发布。
Vision 功能限制
- 禁止使用 screenshot 图片识别功能(OpenAI 组织未开启 vision)
- 所有页面信息必须通过文本/DOM 抽取获取(evaluate/snapshot)
- 遇到图片内容时,只能读取 alt 文本或跳过
Skill 定位
- 这是一个 全自动互动 skill:
- 自动:找内容 + 点赞 + 填评论 + 直接发送
- 不询问用户确认,输入完成后立即发送
1. 使用场景与触发条件
当用户出现类似需求时使用本 skill:
- 「去 X 时间线上找几条技术帖子评论」
- 「在 X 首页互动几条 BTC 相关内容」
- 「帮我在 X 推荐页发几条技术评论」
- 「去 X 首页找些 AI/区块链/开发相关的内容评论」
默认假设:
- 话题范围:技术类(AI、automation、开发、工具等)+ BTC/加密货币相关;
- 条数
N = 3,除非用户另行指定;
- 语言策略:根据原推文语言自动匹配(中文推文→中文评论,其他语言→英文评论);
- 只评论,不点赞。
2. 高层流程
确定参数
- 话题筛选:技术类(AI、agent、automation、开发工具、编程等)+ BTC/加密货币相关;
- 条数
N:默认 3;
- 互动动作:只评论,不点赞;
- 语气:
- 默认:专业、友好、偏工程视角;
- 对敏感话题(政治/战争等)要中立、反战、强调查证。
- 语言策略:
- 检测原推文主要语言(通过文本中汉字占比、标点符号等判断);
- 中文推文(汉字占比 > 30%)→ 用中文回复;
- 其他语言 → 用英文回复;
- 技术术语始终保持英文(如 API、Docker、LLM 等)。
打开 Home timeline
https://x.com/home
通过 evaluate 抓取候选 tweets(增强版:包含互动数据)
在 Home timeline 执行类似脚本,拿前若干条并获取互动数据:
() => {
const N = 15; // 多拉一些以便筛选优质内容
const articles = Array.from(document.querySelectorAll('article[role="article"]'));
const picked = articles.slice(0, N);
const tweets = picked.map(article => {
const linkEl = article.querySelector('a[href*="/status/"]');
const href = linkEl ? linkEl.href : null;
const textEl = article.querySelector('div[data-testid="tweetText"]');
const text = textEl ? textEl.innerText : '';
const authorEl = article.querySelector('div[data-testid="User-Name"] span');
const author = authorEl ? authorEl.innerText : '';
// 获取互动数据(reply/retweet/like 计数)
const replyEl = article.querySelector('[data-testid="reply"]');
const retweetEl = article.querySelector('[data-testid="retweet"]');
const likeEl = article.querySelector('[data-testid="like"]');
const replies = replyEl?.getAttribute('aria-label')?.match(/\d+/)?.[0] || '0';
const retweets = retweetEl?.getAttribute('aria-label')?.match(/\d+/)?.[0] || '0';
const likes = likeEl?.getAttribute('aria-label')?.match(/\d+/)?.[0] || '0';
// 获取发布时间(相对时间)
const timeEl = article.querySelector('time');
const timeAgo = timeEl?.getAttribute('datetime') || '';
return {
href, text, author,
replies: parseInt(replies),
retweets: parseInt(retweets),
likes: parseInt(likes),
timeAgo
};
}).filter(t => t.href && t.text);
return { ok: true, tweets };
}
模型侧基于多维度筛选出 N 条最值得互动的推文。
筛选标准(优先级从高到低):
优质账号优先:
- 已认证账号(蓝V/金V)
- 知名技术博主/开发者/加密 KOL
- 粉丝量 > 1k 或互动率高的账号
新鲜度优先:
- 优先选择 < 2 小时内发布的推文
- 次优:2-6 小时内
- 尽量避免 > 24 小时的旧内容
互动起飞阶段:
- 理想区间:10-100 条回复,说明话题有热度但未过度拥挤
- 点赞/转发比例合理(非刷量)
- 避免:0 互动(冷清)或 >500 互动(已饱和)
话题相关性:
- 技术类:AI、agent、automation、OpenClaw、开发工具、编程、框架、API、cloud、DevOps 等
- BTC 相关:Bitcoin、BTC、加密货币、区块链、DeFi、Web3 等
- 排除:纯政治、八卦、娱乐、广告推广等无关内容
为每条生成高质量评论文案
评论质量标准(必须满足至少一项):
提供额外价值:
- 补充原推文未提及的技术细节、实践经验
- 分享相关工具/资源/文档链接
- 提出可操作的改进建议
- 例:「这个方案不错,如果结合 XX 工具的 YY 功能,可以进一步优化 ZZ 环节」
制造对立(建设性):
- 提出不同观点但附带论据
- 指出潜在问题或边界条件
- 挑战假设但保持尊重
- 例:「不完全同意,实际场景中 XX 可能成为瓶颈,因为……」
提问引导:
- 提出深入的技术问题
- 请求具体细节或使用体验
- 引导作者展开讨论
- 例:「好奇你们是怎么处理 XX 场景的?我们遇到了 YY 问题」
求证验证:
- 对数据/结论提出验证需求
- 分享类似场景的对比结果
- 请求开源/文档链接
- 例:「有相关的 benchmark 数据吗?我们测试下来 XX 指标差异较大」
幽默共鸣:
- 用梗或类比增强表达
- 自嘲式认同(「我也踩过这个坑」)
- 轻松但不失专业
- 例:「哈哈这个 bug 是程序员的必经之路,上次我因为这个 debug 到凌晨三点」
格式与长度要求:
- 字数限制(硬约束):
- 普通用户(非 Premium):最多 280 字符
- 中文评论建议:50-120 字(约 150-360 字符,安全范围内)
- 严格控制:生成评论后必须验证字符数 ≤ 280,超过则缩减
- 结构:
- 开头:直接切入观点/问题(不要「很有意思」「很棒」等空话)
- 主体:1-2 句核心论述或具体细节
- 结尾:可选的问句/行动号召(如「你怎么看?」「期待开源」)
- 格式优化:
- 必要时用换行分段(最多 2 段)
- 技术术语用英文或代码格式(如
API、Docker)
- 避免过度使用 emoji(最多 1-2 个,且有实际语义)
互动闭环策略:
- 优先评论有回复习惯的作者(查看其它回复中作者是否活跃)
- 在评论中留钩子:提问、@相关账号、预告后续分享
- 例:「我们团队也在探索类似方案,改天整理个完整对比发出来 @你」
- 如果作者回复,在下一轮 cron job 时优先回访(需要维护一个「待回访列表」)
负面示例(避免):
❌ 「很有意思,学习了!」(无实质内容)
❌ 「666」「牛逼」(无价值的符号)
❌ 「同意楼上」(重复他人观点)
❌ 纯广告/自我推广(「我们的产品也能做到」)
❌ 过长的技术教程(超过 150 字,应该拆成独立推文)
正面示例(已验证字符数 ≤ 280):
✅ 「这个架构的关键在于状态管理,我们之前用 Redis 做分布式锁遇到过脑裂问题,后来改用 etcd 的 lease 机制才彻底解决。你们是怎么处理的?」(62 字/约 186 字符,提供价值 + 提问)
✅ 「不完全认同"BTC 只是投机工具",真正的价值在于抗审查和去中心化。当然现阶段 90% 的人确实只是炒币,这是两码事。」(53 字/约 159 字符,制造对立但有论据)
✅ 「哈哈上次我也因为忘了清 Docker cache 导致镜像大小暴涨到 2GB,最后发现是 node_modules 被打进去了。多阶段构建真的能救命。」(62 字/约 186 字符,幽默共鸣 + 实践经验)
对选中 tweets 自动点赞(本版本取消点赞)
不再执行点赞动作,直接进入评论流程。
进入详情页 + 打开回复框 + 填入评论
对每条选中的 tweet:
使用 browser.navigate 进入详情页:
{
"action": "navigate",
"targetId": "<page-id>",
"url": "https://x.com/<user>/status/<id>"
}
使用 evaluate 在详情页:
- 找到
article[role="article"];
- 点击
[data-testid="reply"] 打开回复弹窗;
- 等待
div[role="dialog"] div[role="textbox"][contenteditable="true"] 出现。
在文本框内使用 browser 原生 type 动作 + slowly: true 输入评论内容:
⚠️ 重要:必须使用 browser 的原生 type 动作配合 slowly: true,而不是 evaluate 里的 JS 模拟输入!
X 的 Draft.js 编辑器会校验输入事件的来源,只有通过 Playwright/CDP 的真实键盘输入才能让 Reply 按钮亮起。
正确做法:
先用 snapshot 获取回复对话框中 textbox 的 ref:
browser snapshot labels=false targetId=<page-id>
找到 textbox "Post text" [ref=eXXX]
用 browser act 的 type 动作,必须带 slowly: true:
{
"action": "act",
"profile": "openclaw",
"targetId": "<page-id>",
"request": {
"kind": "type",
"ref": "eXXX",
"text": "你的评论内容",
"slowly": true
}
}
错误做法(Reply 按钮会保持灰色):
- 用
evaluate + execCommand('insertText', ...)
- 用
evaluate + 手动 dispatch KeyboardEvent/InputEvent
- 用
type 但不带 slowly: true
验证按钮状态:
输入完成后,可以用 evaluate 检查 Reply 按钮是否亮起:
(() => {
const dialog = document.querySelector('div[role="dialog"]');
const btns = dialog?.querySelectorAll('button');
for (const btn of btns || []) {
if (btn.innerText?.trim() === 'Reply') {
return { disabled: btn.disabled, ariaDisabled: btn.getAttribute('aria-disabled') };
}
}
return { found: false };
})()
当 disabled: false 且 ariaDisabled: null 时,按钮可点击。
输入后直接发送(默认行为):
使用 slowly: true 输入后,Reply 按钮应该已经亮起。默认直接点击发送,不询问用户确认。
发送方式:用 evaluate 点击 Reply 按钮:
(() => {
const dialog = document.querySelector('div[role="dialog"]');
const btns = dialog?.querySelectorAll('button');
for (const btn of btns || []) {
if (btn.innerText?.trim() === 'Reply' && !btn.disabled) {
btn.click();
return 'clicked Reply';
}
}
return 'not found or disabled';
})()
发送后等待 2 秒,检查对话框是否关闭(dialogExists: false)来确认发送成功。
3. 对用户的反馈格式
完成互动后,向用户做一个结构化汇报,例如:
- 本轮使用
profile=openclaw,已在托管浏览器中登录 X;
- 已自动执行:
- 筛选范围:X 首页推荐时间线(Home timeline)
- 筛选策略:优质账号 + 最新发布(< 2h)+ 互动起飞阶段(10-100 回复)
- 筛选标准:技术类(AI/agent/开发工具等)+ BTC/加密货币相关
- 评论质量:每条评论附带价值类型标签(提供价值/制造对立/提问/求证/幽默共鸣)
- 发送状态:
- 以「对话框是否关闭」作为发送成功的判断依据;
- 如果对话框关闭,视为发送成功。
示例(自然语言):
本轮在 X 首页完成了 2 条高质量互动:
@某 AI 开发者(1 小时前,35 回复)
话题:LangChain vs Semantic Kernel
评论:「这两个框架的定位其实不同,LangChain 偏快速原型,SK 更适合企业级。我们项目里混用,用 LC 做 POC,SK 做生产。你们有考虑过混合方案吗?」
类型:提供价值 + 提问(110 字)
@某加密分析师(30 分钟前,18 回复)
话题:BTC 短期波动分析
评论:「技术分析在加密市场作用有限,尤其是链上数据和宏观政策更有预测力。上次 ETF 通过前夕就是典型案例,TA 根本没捕捉到。」
类型:制造对立(68 字)
4. 互动闭环跟踪(可选增强功能)
为了实现真正的互动闭环,可以在 workspace 中维护一个状态文件:
文件路径:~/.openclaw/workspace/skills/x-comment/interaction-state.json
结构示例:
{
"pendingReplies": [
{
"tweetUrl": "https://x.com/user/status/123456",
"myCommentTime": 1773533956000,
"author": "@某开发者",
"topic": "AI agent 框架",
"checkCount": 0,
"lastChecked": null
}
],
"completedThreads": [
{
"tweetUrl": "https://x.com/user/status/789012",
"rounds": 3,
"finalStatus": "author-replied-back"
}
]
}
跟踪策略:
- 每次发送评论后,将 tweet 加入
pendingReplies
- 在后续 cron job 中:
- 检查
pendingReplies 中是否有作者回复(通过访问 tweet 详情页,检查作者是否回复了我的评论)
- 如果有回复,优先对这些内容进行二次互动
- 如果 3 次检查后仍无回复,移入
completedThreads 标记为 no-author-reply
- 二次互动评论同样遵循「高质量」标准,避免简单的「感谢回复」
5. 清理与关闭
完成所有评论后,关闭浏览器 tab:
{
"action": "close",
"targetId": "<page-id>"
}
这样可以释放资源,避免后台累积过多标签页。
6. 技术要点总结
- 输入方式:必须用
browser act + type + slowly: true,不能用 evaluate 脚本注入
- 发送方式:用 evaluate 点击 Reply 按钮(
btn.click())
- 成功判断:对话框关闭(
dialogExists: false)即为发送成功
- 默认行为:全自动,输入完成后直接发送,不询问用户确认
- 质量优先:宁可少评论,也要保证每条都是高质量(50-150 字,有实质内容)
- 互动闭环:维护状态文件跟踪作者回复,优先二次互动形成对话
- 任务结束:所有评论完成后关闭浏览器 tab,释放资源
1---2name: x-comment3description: 在 X(Twitter/X.com)的推荐时间线(Home timeline)上自动浏览并评论技术类和 BTC 相关推文。 用户说「去 X 时间线上找几条技术帖子评论」「在 X 首页互动几条 BTC 相关内容」 「帮我在 X 推荐页发几条技术评论」时触发本 skill。不进行点赞,只发布评论。4---56# x-comment —— X 时间线自动评论78本 skill 固化的是我们在 `profile=openclaw` 环境下实测的一套流程:910- 浏览 X 首页推荐时间线(Home timeline)11- 筛选技术类、BTC 相关的推文12- 自动完成:打开 reply + **模拟真人慢速打字**填入评论 + **直接发送**1314> 核心原则:**全自动完成互动,输入评论后直接发送,不询问用户确认。只评论不点赞。**1516---1718## 0. 环境约束与边界19201. **浏览器与 profile**21 - 使用托管浏览器 `profile="openclaw"`,通过 HTTP 代理访问 X;22 - 需要保证该浏览器 profile 内已经登录目标 X 账号,否则不会出现 like/reply 按钮。23242. **交互能力现状**25 - 已实测可行:26 - 能在搜索结果页/详情页获取完整 DOM(含 `data-testid="like"/"reply"`)。27 - 能点击 like / reply 按钮。28 - 能用 `browser act` + `type` + `slowly: true` 模拟真人键盘输入,让 Reply 按钮亮起。29 - 能自动点击发送按钮完成评论发布。30313. **Vision 功能限制**32 - **禁止使用 screenshot 图片识别功能**(OpenAI 组织未开启 vision)33 - 所有页面信息必须通过文本/DOM 抽取获取(evaluate/snapshot)34 - 遇到图片内容时,只能读取 alt 文本或跳过35364. **Skill 定位**37 - 这是一个 **全自动互动 skill**:38 - 自动:找内容 + 点赞 + 填评论 + **直接发送**39 - **不询问用户确认,输入完成后立即发送**4041---4243## 1. 使用场景与触发条件4445当用户出现类似需求时使用本 skill:4647- 「去 X 时间线上找几条技术帖子评论」48- 「在 X 首页互动几条 BTC 相关内容」49- 「帮我在 X 推荐页发几条技术评论」50- 「去 X 首页找些 AI/区块链/开发相关的内容评论」5152默认假设:5354- 话题范围:**技术类(AI、automation、开发、工具等)+ BTC/加密货币相关**;55- 条数 `N = 3`,除非用户另行指定;56- 语言策略:**根据原推文语言自动匹配**(中文推文→中文评论,其他语言→英文评论);57- **只评论,不点赞**。5859---6061## 2. 高层流程62631. 确定参数64 - 话题筛选:技术类(AI、agent、automation、开发工具、编程等)+ BTC/加密货币相关;65 - 条数 `N`:默认 3;66 - 互动动作:**只评论,不点赞**;67 - 语气:68 - 默认:专业、友好、偏工程视角;69 - 对敏感话题(政治/战争等)要中立、反战、强调查证。70 - 语言策略:71 - **检测原推文主要语言**(通过文本中汉字占比、标点符号等判断);72 - 中文推文(汉字占比 > 30%)→ 用中文回复;73 - 其他语言 → 用英文回复;74 - 技术术语始终保持英文(如 API、Docker、LLM 等)。75762. 打开 Home timeline7778 ```text79 https://x.com/home80 ```8182 - 使用:8384 ```jsonc85 {86 "action": "open",87 "profile": "openclaw",88 "target": "host",89 "targetUrl": "https://x.com/home"90 }91 ```92933. 通过 `evaluate` 抓取候选 tweets(增强版:包含互动数据)9495 在 Home timeline 执行类似脚本,拿前若干条并获取互动数据:9697 ```js98 () => {99 const N = 15; // 多拉一些以便筛选优质内容100 const articles = Array.from(document.querySelectorAll('article[role="article"]'));101 const picked = articles.slice(0, N);102 const tweets = picked.map(article => {103 const linkEl = article.querySelector('a[href*="/status/"]');104 const href = linkEl ? linkEl.href : null;105 const textEl = article.querySelector('div[data-testid="tweetText"]');106 const text = textEl ? textEl.innerText : '';107 const authorEl = article.querySelector('div[data-testid="User-Name"] span');108 const author = authorEl ? authorEl.innerText : '';109 110 // 获取互动数据(reply/retweet/like 计数)111 const replyEl = article.querySelector('[data-testid="reply"]');112 const retweetEl = article.querySelector('[data-testid="retweet"]');113 const likeEl = article.querySelector('[data-testid="like"]');114 const replies = replyEl?.getAttribute('aria-label')?.match(/\d+/)?.[0] || '0';115 const retweets = retweetEl?.getAttribute('aria-label')?.match(/\d+/)?.[0] || '0';116 const likes = likeEl?.getAttribute('aria-label')?.match(/\d+/)?.[0] || '0';117 118 // 获取发布时间(相对时间)119 const timeEl = article.querySelector('time');120 const timeAgo = timeEl?.getAttribute('datetime') || '';121 122 return { 123 href, text, author, 124 replies: parseInt(replies), 125 retweets: parseInt(retweets), 126 likes: parseInt(likes),127 timeAgo128 };129 }).filter(t => t.href && t.text);130 return { ok: true, tweets };131 }132 ```133134 模型侧基于多维度筛选出 `N` 条**最值得互动**的推文。135136 **筛选标准(优先级从高到低):**137 138 1. **优质账号优先**:139 - 已认证账号(蓝V/金V)140 - 知名技术博主/开发者/加密 KOL141 - 粉丝量 > 1k 或互动率高的账号142 143 2. **新鲜度优先**:144 - 优先选择 < 2 小时内发布的推文145 - 次优:2-6 小时内146 - 尽量避免 > 24 小时的旧内容147 148 3. **互动起飞阶段**:149 - 理想区间:10-100 条回复,说明话题有热度但未过度拥挤150 - 点赞/转发比例合理(非刷量)151 - 避免:0 互动(冷清)或 >500 互动(已饱和)152 153 4. **话题相关性**:154 - 技术类:AI、agent、automation、OpenClaw、开发工具、编程、框架、API、cloud、DevOps 等155 - BTC 相关:Bitcoin、BTC、加密货币、区块链、DeFi、Web3 等156 - 排除:纯政治、八卦、娱乐、广告推广等无关内容1571584. 为每条生成高质量评论文案159160 **评论质量标准(必须满足至少一项):**161 162 1. **提供额外价值**:163 - 补充原推文未提及的技术细节、实践经验164 - 分享相关工具/资源/文档链接165 - 提出可操作的改进建议166 - 例:「这个方案不错,如果结合 XX 工具的 YY 功能,可以进一步优化 ZZ 环节」167 168 2. **制造对立(建设性)**:169 - 提出不同观点但附带论据170 - 指出潜在问题或边界条件171 - 挑战假设但保持尊重172 - 例:「不完全同意,实际场景中 XX 可能成为瓶颈,因为……」173 174 3. **提问引导**:175 - 提出深入的技术问题176 - 请求具体细节或使用体验177 - 引导作者展开讨论178 - 例:「好奇你们是怎么处理 XX 场景的?我们遇到了 YY 问题」179 180 4. **求证验证**:181 - 对数据/结论提出验证需求182 - 分享类似场景的对比结果183 - 请求开源/文档链接184 - 例:「有相关的 benchmark 数据吗?我们测试下来 XX 指标差异较大」185 186 5. **幽默共鸣**:187 - 用梗或类比增强表达188 - 自嘲式认同(「我也踩过这个坑」)189 - 轻松但不失专业190 - 例:「哈哈这个 bug 是程序员的必经之路,上次我因为这个 debug 到凌晨三点」191192 **格式与长度要求:**193 194 - **字数限制(硬约束)**:195 - **普通用户(非 Premium):最多 280 字符**196 - 中文评论建议:50-120 字(约 150-360 字符,安全范围内)197 - **严格控制**:生成评论后必须验证字符数 ≤ 280,超过则缩减198 - **结构**:199 - 开头:直接切入观点/问题(不要「很有意思」「很棒」等空话)200 - 主体:1-2 句核心论述或具体细节201 - 结尾:可选的问句/行动号召(如「你怎么看?」「期待开源」)202 - **格式优化**:203 - 必要时用换行分段(最多 2 段)204 - 技术术语用英文或代码格式(如 `API`、`Docker`)205 - 避免过度使用 emoji(最多 1-2 个,且有实际语义)206207 **互动闭环策略:**208 209 - 优先评论**有回复习惯的作者**(查看其它回复中作者是否活跃)210 - 在评论中**留钩子**:提问、@相关账号、预告后续分享211 - 例:「我们团队也在探索类似方案,改天整理个完整对比发出来 @你」212 - 如果作者回复,**在下一轮 cron job 时优先回访**(需要维护一个「待回访列表」)213214 **负面示例(避免):**215 216 ❌ 「很有意思,学习了!」(无实质内容) 217 ❌ 「666」「牛逼」(无价值的符号) 218 ❌ 「同意楼上」(重复他人观点) 219 ❌ 纯广告/自我推广(「我们的产品也能做到」) 220 ❌ 过长的技术教程(超过 150 字,应该拆成独立推文)221222 **正面示例(已验证字符数 ≤ 280):**223 224 ✅ 「这个架构的关键在于状态管理,我们之前用 Redis 做分布式锁遇到过脑裂问题,后来改用 etcd 的 lease 机制才彻底解决。你们是怎么处理的?」(62 字/约 186 字符,提供价值 + 提问)225 226 ✅ 「不完全认同"BTC 只是投机工具",真正的价值在于抗审查和去中心化。当然现阶段 90% 的人确实只是炒币,这是两码事。」(53 字/约 159 字符,制造对立但有论据)227 228 ✅ 「哈哈上次我也因为忘了清 Docker cache 导致镜像大小暴涨到 2GB,最后发现是 node_modules 被打进去了。多阶段构建真的能救命。」(62 字/约 186 字符,幽默共鸣 + 实践经验)2292305. ~~对选中 tweets 自动点赞~~(本版本取消点赞)231232 **不再执行点赞动作,直接进入评论流程。**2332346. 进入详情页 + 打开回复框 + 填入评论235236 对每条选中的 tweet:237238 1. 使用 `browser.navigate` 进入详情页:239240 ```jsonc241 {242 "action": "navigate",243 "targetId": "<page-id>",244 "url": "https://x.com/<user>/status/<id>"245 }246 ```247248 2. 使用 `evaluate` 在详情页:249 - 找到 `article[role="article"]`;250 - 点击 `[data-testid="reply"]` 打开回复弹窗;251 - 等待 `div[role="dialog"] div[role="textbox"][contenteditable="true"]` 出现。252253 3. 在文本框内**使用 browser 原生 type 动作 + slowly: true** 输入评论内容:254255 **⚠️ 重要:必须使用 browser 的原生 `type` 动作配合 `slowly: true`,而不是 evaluate 里的 JS 模拟输入!**256257 X 的 Draft.js 编辑器会校验输入事件的来源,只有通过 Playwright/CDP 的真实键盘输入才能让 Reply 按钮亮起。258259 **正确做法:**260261 1. 先用 `snapshot` 获取回复对话框中 textbox 的 ref:262 ```263 browser snapshot labels=false targetId=<page-id>264 ```265 找到 `textbox "Post text" [ref=eXXX]`266267 2. 用 `browser act` 的 `type` 动作,**必须带 `slowly: true`**:268 ```jsonc269 {270 "action": "act",271 "profile": "openclaw",272 "targetId": "<page-id>",273 "request": {274 "kind": "type",275 "ref": "eXXX",276 "text": "你的评论内容",277 "slowly": true278 }279 }280 ```281282 **错误做法(Reply 按钮会保持灰色):**283 - 用 `evaluate` + `execCommand('insertText', ...)`284 - 用 `evaluate` + 手动 dispatch KeyboardEvent/InputEvent285 - 用 `type` 但不带 `slowly: true`286287 **验证按钮状态:**288 输入完成后,可以用 evaluate 检查 Reply 按钮是否亮起:289 ```js290 (() => {291 const dialog = document.querySelector('div[role="dialog"]');292 const btns = dialog?.querySelectorAll('button');293 for (const btn of btns || []) {294 if (btn.innerText?.trim() === 'Reply') {295 return { disabled: btn.disabled, ariaDisabled: btn.getAttribute('aria-disabled') };296 }297 }298 return { found: false };299 })()300 ```301 当 `disabled: false` 且 `ariaDisabled: null` 时,按钮可点击。302303 4. **输入后直接发送(默认行为)**:304305 使用 `slowly: true` 输入后,Reply 按钮应该已经亮起。**默认直接点击发送,不询问用户确认。**306307 发送方式:用 `evaluate` 点击 Reply 按钮:308 ```js309 (() => {310 const dialog = document.querySelector('div[role="dialog"]');311 const btns = dialog?.querySelectorAll('button');312 for (const btn of btns || []) {313 if (btn.innerText?.trim() === 'Reply' && !btn.disabled) {314 btn.click();315 return 'clicked Reply';316 }317 }318 return 'not found or disabled';319 })()320 ```321322 发送后等待 2 秒,检查对话框是否关闭(`dialogExists: false`)来确认发送成功。323324---325326## 3. 对用户的反馈格式327328完成互动后,向用户做一个结构化汇报,例如:329330- 本轮使用 `profile=openclaw`,已在托管浏览器中登录 X;331- 已自动执行:332 - 筛选范围:X 首页推荐时间线(Home timeline)333 - 筛选策略:优质账号 + 最新发布(< 2h)+ 互动起飞阶段(10-100 回复)334 - 筛选标准:技术类(AI/agent/开发工具等)+ BTC/加密货币相关335 - 评论质量:每条评论附带价值类型标签(提供价值/制造对立/提问/求证/幽默共鸣)336- 发送状态:337 - 以「对话框是否关闭」作为发送成功的判断依据;338 - 如果对话框关闭,视为发送成功。339340示例(自然语言):341342> 本轮在 X 首页完成了 2 条高质量互动:343> 344> 1. **@某 AI 开发者**(1 小时前,35 回复) 345> 话题:LangChain vs Semantic Kernel 346> 评论:「这两个框架的定位其实不同,LangChain 偏快速原型,SK 更适合企业级。我们项目里混用,用 LC 做 POC,SK 做生产。你们有考虑过混合方案吗?」 347> 类型:**提供价值 + 提问**(110 字)348> 349> 2. **@某加密分析师**(30 分钟前,18 回复) 350> 话题:BTC 短期波动分析 351> 评论:「技术分析在加密市场作用有限,尤其是链上数据和宏观政策更有预测力。上次 ETF 通过前夕就是典型案例,TA 根本没捕捉到。」 352> 类型:**制造对立**(68 字)353354---355356## 4. 互动闭环跟踪(可选增强功能)357358为了实现真正的互动闭环,可以在 workspace 中维护一个状态文件:359360**文件路径**:`~/.openclaw/workspace/skills/x-comment/interaction-state.json`361362**结构示例**:363364```json365{366 "pendingReplies": [367 {368 "tweetUrl": "https://x.com/user/status/123456",369 "myCommentTime": 1773533956000,370 "author": "@某开发者",371 "topic": "AI agent 框架",372 "checkCount": 0,373 "lastChecked": null374 }375 ],376 "completedThreads": [377 {378 "tweetUrl": "https://x.com/user/status/789012",379 "rounds": 3,380 "finalStatus": "author-replied-back"381 }382 ]383}384```385386**跟踪策略**:3873881. 每次发送评论后,将 tweet 加入 `pendingReplies`3892. 在后续 cron job 中:390 - 检查 `pendingReplies` 中是否有作者回复(通过访问 tweet 详情页,检查作者是否回复了我的评论)391 - 如果有回复,**优先对这些内容进行二次互动**392 - 如果 3 次检查后仍无回复,移入 `completedThreads` 标记为 `no-author-reply`3933. 二次互动评论同样遵循「高质量」标准,避免简单的「感谢回复」394395---396397## 5. 清理与关闭398399完成所有评论后,**关闭浏览器 tab**:400401```jsonc402{403 "action": "close",404 "targetId": "<page-id>"405}406```407408这样可以释放资源,避免后台累积过多标签页。409410---411412## 6. 技术要点总结413414- **输入方式**:必须用 `browser act` + `type` + `slowly: true`,不能用 evaluate 脚本注入415- **发送方式**:用 evaluate 点击 Reply 按钮(`btn.click()`)416- **成功判断**:对话框关闭(`dialogExists: false`)即为发送成功417- **默认行为**:全自动,输入完成后直接发送,不询问用户确认418- **质量优先**:宁可少评论,也要保证每条都是高质量(50-150 字,有实质内容)419- **互动闭环**:维护状态文件跟踪作者回复,优先二次互动形成对话420- **任务结束**:所有评论完成后关闭浏览器 tab,释放资源