Skill: jasoncodespace/x-engagement (version 4.1.3)

--- name: x-engagement version: 4.2.0-local description: "X/Twitter 运营辅助。完整 onboarding → Persona 学习 → Browser Relay 浏览器控制 → 记忆系统 → 手动提醒模板 → For You 关注建议 → Following 互动建议" --- # X 运营辅助 Skill v4.2 ## 快速开始 **触发条件：** - "刷推 [时间]" - "运营推特 [时间]" - "去X上互动 [时间]" **首次运行：** 自动进入 Onboarding 流程（详见 `docs/onboarding.md`） **后续运行：** 读取配置 → 生成互动建议 → 用户确认后执行 --- ## 文档结构 ``` x-engagement/ ├── SKILL.md # 主入口（本文件） ├── docs/ │ ├── onboarding.md # Onboarding 流程 │ ├── browser-operations.md # 浏览器操作模块（基于 Browser Relay） │ ├── comment-rules.md # 评论规则（重要！防止错误） │ ├── human-behavior.md # 自然节奏与确认规范 │ ├── memory-system.md # 记忆系统设计 │ ├── cron-jobs.md # 手动提醒与维护 │ ├── comment-generation.md # 评论生成逻辑 │ └── natural-language-parser.md # 自然语言时间解析 ├── playbooks/ │ ├── comment-strategies.md # 评论策略（有效/无效） │ └── changelog.md # 策略变更记录 ├── data/ │ └── engagement/ │ └── YYYY-MM-DD.json # 每日评论数据 ├── templates/ │ ├── persona.md # Persona 模板 │ ├── config.json # 配置模板 │ └── daily-log.md # 每日日志模板 └── scripts/ ├── setup-cron.sh # 生成手动提醒模板 ├── check-cron.sh # 检查本地运行状态 └── daily-review.sh # 每日复盘脚本 ``` --- ## 核心功能 ### 1. Onboarding（首次运行） **5个阶段：** 1. 浏览器连接 + 登录检查 2. 选择 Persona（自己或其他账号） 3. 学习 Persona（抓取100条 → 生成描述） 4. 刷推习惯配置 5. 保存配置 **详见：** `docs/onboarding.md` --- ### 2. 自然节奏与确认 **核心原则：** 保持自然节奏，但不追求伪装或规避检测。 **包含：** - 阅读与判断节奏建议 - 点赞/关注/评论二次确认 - 频率限制 - 人工审核后再执行写操作 **详见：** `docs/human-behavior.md` --- ### 3. 记忆系统 **三层记忆：** ``` memory/daily/hotspots/ ├── .onboarding_complete # Onboarding 标记 ├── .config.json # 用户配置 ├── personas/ │ └── [handle].md # Persona 描述 ├── events/ # 重大事件（永久） ├── tables/ # 每日热点（7天） └── history/ ├── comments/ # 评论历史（避免自相矛盾） └── daily/ # 每日日志 ``` **关键功能：** - 记录每次评论内容 - 记录用户说过的话（如"昨天出去吃饭了"） - 评论前检查历史，避免矛盾 **详见：** `docs/memory-system.md` --- ### 4. 手动提醒与维护 **默认策略：** - 不自动安装 cron - 不自动修改 `crontab` - 仅生成手动提醒模板 **可手动执行的维护项：** - 每日热点总结 - 记忆清理预览 - 记忆清理执行（需显式 `--apply`） **生成手动模板：** ```bash ./scripts/setup-cron.sh ``` **详见：** `docs/cron-jobs.md` --- ### 5. 刷推流程 **⚠️ 重要规则（必须遵守）：** 1. **只在 Following 的 Recent 页面评论**（不是 Popular） 2. **评论前检查历史**（避免重复评论同一博主） 3. **记录所有评论**（保存到历史文件） 4. **评论发送前必须得到用户确认** **详见：** `docs/comment-rules.md`（必读！） **For You 页面：** 1. 浏览（自然阅读节奏） 2. 关注建议（根据配置条件） **Following 页面：** 1. 确保是 Recent（不是 Popular） 2. 点赞建议（有价值的推文） 3. 评论建议（2小时内，使用 persona 风格） 4. 用户确认后执行 5. 记录评论到历史（避免重复） **详见：** `docs/comment-generation.md` --- ### 6. 浏览器操作（基于 Browser Relay） **使用 `browser-relay-cli`：** - 连接你们自己的 Browser Relay 运行时 - 复用本地已登录 Chrome/Chromium - 支持 DOM 优先、截图兜底的受控操作 - 仓库：`https://github.com/jasonCodeSpace/browser-relay` **核心操作：** - 读取推文 - 生成评论建议 - 点赞/关注/评论确认后执行 - 滚动与截图验证 **安全边界：** - 不默认安装持久任务 - 不默认自动发送评论 - 不把 Browser Relay 当 stealth bot **详见：** `docs/browser-operations.md` --- ## 使用示例 ### 首次使用 ``` 用户: 刷推 Bot: 开始 Onboarding... 1. 检查浏览器... 2. 请选择 persona... 3. 学习中... 4. 配置刷推习惯... 5. 完成！开始刷推... ``` ### 后续使用 ``` 用户: 刷推半小时 Bot: 读取配置... For You: 给出 6 个建议关注账号 Following: 给出 6 条候选互动 你确认后我再执行... ``` --- ## 关键特性 | 特性 | 说明 | |------|------| | 完整 Onboarding | 5阶段引导，学习 persona | | 自然节奏与确认 | 阅读节奏、二次确认、频率建议 | | 记忆系统 | 评论历史、用户信息、热点表格 | | 手动维护 | 无自动 cron，仅手动模板 | | 避免矛盾 | 评论前检查历史记录 | | Browser Relay 集成 | 使用本地 browser-relay-cli | --- ## 自我进化系统 ### 核心理念 > 没有记忆的AI，只是一个聪明的工具。 > 有记忆且能进化的AI，才是会成长的伙伴。 ### 进化闭环 ``` 采集数据 → 分析对比 → 得出结论 → 更新规则 → 下次执行 ``` ### 三大机制 **1. Playbook 系统** - `playbooks/comment-strategies.md` - 记录有效/无效策略 - `playbooks/changelog.md` - 记录策略变更 - Agent 可以更新自己的规则 **2. 数据采集** - `data/engagement/YYYY-MM-DD.json` - 每日评论数据 - 记录：时间、作者、内容、结果 - 用于后续分析和优化 **3. 每日复盘（22:00）** - 统计今日数据 - 分析有效策略 - 更新 Playbook - 生成明日建议 - 推送报告给用户 ### 文件结构 ``` x-engagement/ ├── playbooks/ │ ├── comment-strategies.md # 评论策略（有效/无效） │ └── changelog.md # 策略变更记录 ├── data/ │ └── engagement/ │ └── YYYY-MM-DD.json # 每日评论数据 └── scripts/ └── daily-review.sh # 每日复盘脚本 ``` ### 使用示例 **Agent 学习过程**： 1. 发现「妙啊」评论效果好 2. 在 Playbook 中记录：「妙啊」适用于技术分享，数据支撑：2026-03-02 3. 下次刷推时读取这条规则 4. 考虑在类似推文上使用相同策略 **进化效果**： - Agent 越用越聪明 - 自动学习什么评论有效 - 持续优化策略 - 避免重复错误 --- ## 必读文档 按顺序阅读： 1. `docs/onboarding.md` - 了解首次运行流程 2. `docs/human-behavior.md` - 了解人类行为模拟 3. `docs/memory-system.md` - 了解记忆系统 4. `docs/comment-generation.md` - 了解评论生成 --- *版本: 4.0.0* *更新: 2026-03-02* *改进: 结构化设计 + 记忆系统 + 定时任务 + 人类行为规范*

path: _meta.json
content: ownerId: kn7a9dynkxbnn6qvaa6ygkdjyd8187b4
slug: x-engagement
version: 4.2.0-local
publishedAt: 1772515612957

path: runtime/manual-reminders.txt
content: # x-engagement manual reminder templates
#
# This file is informational only.
# It does not modify crontab, launchd, or any other scheduler.
#
# Recommended manual routines:
#
# 1. Daily hotspot review
#    Run manually when needed:
#    cd "/Users/haochengwang/Desktop/hermes/x-engagement-src" && ./scripts/check-cron.sh
#
# 2. Memory cleanup preview
#    cd "/Users/haochengwang/Desktop/hermes/x-engagement-src" && ./scripts/cleanup-memory.sh
#
# 3. Memory cleanup apply
#    cd "/Users/haochengwang/Desktop/hermes/x-engagement-src" && ./scripts/cleanup-memory.sh --apply
#
# If you later decide to create OS-level reminders, do it manually after review.


path: playbooks/changelog.md
content: # x-engagement 策略变更记录

## 2026-03-02

### 每日复盘 (14:07)

**统计数据**：
- 总评论数：3
- 成功数：2 (66.7%)
- 错误数：1 (33.3%)

**有效策略分析**：
1. **简短认同（中文）**：「妙啊」适用于技术分享
2. **有观点 + 建议**：「确实，小红书流量太少了。B站可以试试」适用于平台讨论
3. **简短认同（英文）**：「Facts. Taste is human.」适用于英文推文

**错误分析**：
1. **测试内容不符合 persona**：评论前需检查 persona 匹配度
2. **重复评论同一博主**：严重错误，必须在评论前检查历史记录
3. **重复评论同一条推文**：同一博主只评论一次，同一条推文只评论一次
4. **理解不深入**（新发现）：没有读取评论区，没有理解具体内容

**明日建议**：
1. ✅ 使用简短认同类评论（中文：「妙啊」「厉害了」，英文：「Facts.」「Agreed.」）
2. ✅ 平台讨论类推文，给出有观点 + 具体建议
3. ❌ 避免重复评论同一博主（必须检查历史）
4. ❌ 避免测试内容（必须符合 persona）
5. ⚠️ 语言必须匹配（中文推文用中文评论，英文推文用英文评论）
6. ⚠️ 评论前必须读取评论区 + 理解具体内容（新增）

**Playbook 更新**：
- 更新无效策略记录
- 添加错误分析和教训
- 新增：理解不深入错误

---

## 2026-03-02

### 新增

**1. Playbook 系统**
- 创建 `playbooks/comment-strategies.md`
- 记录有效/无效评论策略
- 添加评论生成规则

**2. 数据采集**
- 创建 `data/engagement/YYYY-MM-DD.json`
- 记录每次评论的详细数据
- 用于后续分析和优化

**3. 浏览器操作模块**
- 创建 `docs/browser-operations.md`
- 使用 Browser Relay 运行时
- 添加受控确认流与自然节奏建议

**4. 评论规则**
- 创建 `docs/comment-rules.md`
- 三个绝对禁止：Popular 页面、重复博主、重复推文
- 添加历史检查流程

### 修正

**1. Persona 更新**
- 从 edison 更新为 jason
- 更新所有相关文件

**2. 浏览器运行时**
- 切回 Browser Relay
- 原因：统一到自有浏览器控制栈

**3. 评论流程**
- 添加历史检查
- 只在 Following → Recent 页面评论
- 记录所有评论到历史文件

### 发现

**1. Browser Relay 集成**
- 使用 `browser-relay-cli`
- 通过本地已登录浏览器执行受控操作
- 不再依赖 OpenClaw

**2. 评论效果**
- 简短认同：「妙啊」- 适用于技术分享
- 有观点：「确实，小红书流量太少了。B站可以试试」- 适用于平台讨论
- 英文简短：「Facts.」- 适用于英文推文

**3. 错误教训**
- 重复评论同一博主 - 严重错误
- 在 Popular 页面评论 - 错误
- 测试内容不符合 persona - 错误

### 待实现

**1. 自我进化机制**
- [ ] 每日复盘 cronjob（晚上 22:00）
- [ ] 自动分析什么评论有效
- [ ] 自动更新 Playbook
- [ ] 汇报给用户

**2. 数据分析**
- [ ] 统计评论效果（点赞、回复）
- [ ] 识别有效策略
- [ ] 识别无效策略
- [ ] 生成优化建议

**3. Agent 协作**
- [ ] 与其他 Agent 共享数据
- [ ] 学习其他 Agent 的策略
- [ ] 层级汇报

---

## 版本历史

### v1.0 (2026-03-02)
- 初始版本
- 基础评论功能
- Playbook 系统
- 数据采集
- 浏览器操作模块
- 评论规则

---

*最后更新: 2026-03-02 14:06*


path: playbooks/comment-strategies.md
content: # 评论策略 Playbook

## 有效策略

### 2026-03-02

**简短认同类**：
- 「妙啊」- 适用于技术分享、工具推荐
  - 数据：2026-03-02，技术推文，成功
  - 场景：中文推文，工具/技术相关

**有观点类**：
- 「确实，小红书流量太少了。B站可以试试」
  - 数据：2026-03-02，平台讨论推文，成功
  - 场景：平台对比、流量讨论
  - 特点：给出具体建议

**英文简短类**：
- 「Facts. Taste is human.」
  - 数据：2026-03-02，英文推文，成功
  - 场景：英文推文，认同观点
  - 特点：简短、口语化

---

## 无效策略

### 2026-03-02

**测试内容**：
- 「妙啊」
  - 数据：2026-03-02，技术推文，错误
  - 原因：测试内容，不符合 persona
  - 教训：评论前必须检查 persona 匹配度

**重复评论**：
- 「确实，小红书流量太少了。B站可以试试」
  - 数据：2026-03-02，平台讨论，成功但错误
  - 原因：重复评论同一博主同一条推文
  - 教训：必须在评论前检查历史记录，同一博主只评论一次

**理解不深入**：
- 「确实，CodePilot 交互设计很优雅」
  - 数据：2026-03-02，技术分享，成功但质量低
  - 原因：
    1. 没有读取评论区
    2. 没有理解"藏师傅的CodePilot"和普通CodePilot的区别
    3. 评论太简单，没有提到具体功能
    4. 没有提到"藏师傅"或具体特点
  - 教训：
    1. **必须先读取评论区**
    2. **理解推文的具体内容**
    3. **评论要具体，提到关键信息**
    4. **不要只看标题就评论**
  - 正确做法：
    - 读取评论区
    - 理解"藏师傅的CodePilot"的特点
    - 评论示例：
      - "藏师傅的CodePilot 确实不错，Telegram 支持很实用"
      - "2500 Star 了，藏师傅牛逼"
      - "支持 Skill 和 MCP，这个厉害"

**重复评论同一博主**：
- 「确实，小红书流量太少了。B站可以试试」
  - 数据：2026-03-02，平台讨论，成功但错误
  - 原因：重复评论同一博主同一条推文
  - 教训：**严重错误**，必须在评论前检查历史记录，同一博主只评论一次

---

## 待记录

**格式**：
```
- 「评论内容」
  - 数据：日期，推文类型，结果
  - 原因：为什么无效
```

---

## 评论生成规则

### 1. 语言匹配

| 推文语言 | 评论语言 |
|---------|---------|
| 中文 | 中文 |
| 英文 | 英文 |
| 其他 | 英文 |

### 2. 内容类型

| 推文类型 | 推荐评论风格 |
|---------|-------------|
| 技术分享 | 简短认同：「妙啊」「厉害了」 |
| 平台讨论 | 有观点 + 建议 |
| 观点输出 | 认同：「Facts.」「确实」 |
| 新闻资讯 | 不评论（避免政治） |

### 3. 长度规则

- **中文**：5-20字
- **英文**：2-10词
- **避免**：长篇大论、emoji

### 4. 时机规则

- **最佳时间**：推文发布后 1-2 小时
- **避免时间**：深夜（23:00-08:00）
- **间隔**：3-6 分钟随机

---

## 禁止内容

**绝对不要评论**：
1. 政治新闻（伊朗战争、政治争议）
2. 争议性话题（性别议题、宗教）
3. 负面情绪（抱怨、吐槽）
4. 已评论过的博主

---

## 数据跟踪

**记录格式**：
```json
{
  "date": "2026-03-02",
  "comment": "评论内容",
  "author": "@username",
  "tweet_type": "技术分享",
  "language": "中文",
  "result": "success/failed",
  "likes": 0,
  "replies": 0,
  "notes": "备注"
}
```

---

## 更新记录

### 2026-03-02
- 初始创建
- 添加 3 条有效策略
- 添加评论生成规则
- 添加禁止内容

### 2026-03-02 (22:00 每日复盘)
- 更新统计数据：总评论 4 条，成功 3 条，错误 1 条
- 新增错误：重复评论同一博主（严重错误）
- 新增错误：理解不深入（没有读取评论区）
- 更新教训：评论前必须读取评论区 + 理解具体内容
- 更新禁止内容：同一博主只评论一次

---

*最后更新: 2026-03-02 22:00*
*版本: 1.1*


path: docs/natural-language-parser.md
content: # 自然语言时间解析

## 概述

**目的：** 解析用户用自然语言描述的刷推时间设置

**示例：**
- "每天3次，随机时间"
- "早上9点、下午3点、晚上9点"
- "每天随机2-4次"
- "工作日晚上8点，周末随机3次"

---

## 1. 解析流程

```
用户输入 → 分词 → 识别模式 → 提取参数 → 生成配置
```

### 1.1 识别模式

```python
def detect_pattern(user_input):
    # 随机范围
    if "随机" in user_input and "-" in user_input:
        return "random_range"
    
    # 随机固定次数
    elif "随机" in user_input:
        return "random"
    
    # 固定时间
    elif "点" in user_input:
        return "fixed"
    
    # 工作日/周末
    elif "工作日" in user_input or "周末" in user_input:
        return "weekday_weekend"
    
    # 默认
    else:
        return "unknown"
```

---

## 2. 固定时间解析

### 2.1 示例

**用户输入：** "早上9点、下午3点、晚上9点"

**解析：**
```python
def parse_fixed_time(user_input):
    # 提取时间点
    times = []
    
    # 匹配 "X点" 或 "X点Y分"
    import re
    pattern = r'(\d{1,2})点(\d{1,2}分)?'
    matches = re.findall(pattern, user_input)
    
    for hour, minute in matches:
        hour = int(hour)
        minute = int(minute[:-1]) if minute else 0
        
        # 处理中文时间描述
        if "早" in user_input and hour < 12:
            pass  # 早上，不用调整
        elif "下" in user_input and hour < 12:
            hour += 12  # 下午
        elif "晚" in user_input and hour < 12:
            hour += 12  # 晚上
        
        times.append(f"{hour:02d}:{minute:02d}")
    
    return {
        "type": "fixed",
        "times": times
    }
```

**输出：**
```json
{
  "type": "fixed",
  "times": ["09:00", "15:00", "21:00"]
}
```

### 2.2 支持的格式

| 用户输入 | 解析结果 |
|----------|----------|
| "早上9点" | ["09:00"] |
| "9点、15点、21点" | ["09:00", "15:00", "21:00"] |
| "早上9点30分、下午3点45分" | ["09:30", "15:45"] |
| "9am, 3pm, 9pm" | ["09:00", "15:00", "21:00"] |

---

## 3. 随机时间解析

### 3.1 示例

**用户输入：** "每天3次，随机时间"

**解析：**
```python
def parse_random_time(user_input):
    # 提取次数
    import re
    match = re.search(r'(\d+)次', user_input)
    count = int(match.group(1)) if match else 3
    
    # 提取时间范围（如果有）
    time_range = ["09:00", "22:00"]  # 默认
    
    # 返回
    return {
        "type": "random",
        "countPerDay": count,
        "timeRange": time_range
    }
```

**输出：**
```json
{
  "type": "random",
  "countPerDay": 3,
  "timeRange": ["09:00", "22:00"]
}
```

### 3.2 支持的格式

| 用户输入 | 解析结果 |
|----------|----------|
| "每天3次，随机时间" | 随机3次 |
| "随机5次" | 随机5次 |
| "每天随机2次，早上9点到晚上9点" | 随机2次（09:00-21:00） |

---

## 4. 随机范围解析

### 4.1 示例

**用户输入：** "每天随机2-4次"

**解析：**
```python
def parse_random_range(user_input):
    import re
    
    # 提取范围
    match = re.search(r'(\d+)-(\d+)次', user_input)
    if match:
        min_count = int(match.group(1))
        max_count = int(match.group(2))
    else:
        min_count = 2
        max_count = 4
    
    return {
        "type": "random_range",
        "minPerDay": min_count,
        "maxPerDay": max_count,
        "timeRange": ["09:00", "22:00"]
    }
```

**输出：**
```json
{
  "type": "random_range",
  "minPerDay": 2,
  "maxPerDay": 4,
  "timeRange": ["09:00", "22:00"]
}
```

---

## 5. 工作日/周末解析

### 5.1 示例

**用户输入：** "工作日晚上8点，周末随机3次"

**解析：**
```python
def parse_weekday_weekend(user_input):
    result = {
        "type": "weekday_weekend"
    }
    
    # 分割工作日和周末
    parts = user_input.split("，")
    
    for part in parts:
        if "工作日" in part:
            # 解析工作日
            result["weekday"] = parse_time_part(part.replace("工作日", ""))
        elif "周末" in part:
            # 解析周末
            result["weekend"] = parse_time_part(part.replace("周末", ""))
    
    return result

def parse_time_part(text):
    if "随机" in text:
        return parse_random_time(text)
    elif "点" in text:
        return parse_fixed_time(text)
    else:
        return {"type": "unknown"}
```

**输出：**
```json
{
  "type": "weekday_weekend",
  "weekday": {
    "type": "fixed",
    "times": ["20:00"]
  },
  "weekend": {
    "type": "random",
    "countPerDay": 3,
    "timeRange": ["09:00", "22:00"]
  }
}
```

### 5.2 支持的格式

| 用户输入 | 解析结果 |
|----------|----------|
| "工作日晚上8点，周末随机3次" | 工作日固定20:00，周末随机3次 |
| "工作日早上9点和下午3点，周末随机2次" | 工作日09:00和15:00，周末随机2次 |
| "周一到周五随机2次，周末固定10点" | 工作日随机2次，周末固定10:00 |

---

## 6. 统一解析入口

```python
def parse_schedule(user_input):
    """统一解析入口"""
    
    # 1. 识别模式
    pattern = detect_pattern(user_input)
    
    # 2. 根据模式解析
    if pattern == "fixed":
        return parse_fixed_time(user_input)
    elif pattern == "random":
        return parse_random_time(user_input)
    elif pattern == "random_range":
        return parse_random_range(user_input)
    elif pattern == "weekday_weekend":
        return parse_weekday_weekend(user_input)
    else:
        # 无法识别，使用默认
        return {
            "type": "random",
            "countPerDay": 3,
            "timeRange": ["09:00", "22:00"]
        }
```

---

## 7. 完整示例

### 7.1 示例 1

**用户输入：** "每天3次，随机时间"

**解析：**
```json
{
  "type": "random",
  "countPerDay": 3,
  "timeRange": ["09:00", "22:00"]
}
```

**生成的随机时间：**
```
10:23, 14:47, 19:15
```

### 7.2 示例 2

**用户输入：** "早上9点、下午3点、晚上9点"

**解析：**
```json
{
  "type": "fixed",
  "times": ["09:00", "15:00", "21:00"]
}
```

**Cron 任务：**
```
0 9 * * *
0 15 * * *
0 21 * * *
```

### 7.3 示例 3

**用户输入：** "每天随机2-4次"

**解析：**
```json
{
  "type": "random_range",
  "minPerDay": 2,
  "maxPerDay": 4,
  "timeRange": ["09:00", "22:00"]
}
```

**今天生成：** 随机3次
**时间：** 11:05, 15:32, 20:18

### 7.4 示例 4

**用户输入：** "工作日晚上8点，周末随机3次"

**解析：**
```json
{
  "type": "weekday_weekend",
  "weekday": {
    "type": "fixed",
    "times": ["20:00"]
  },
  "weekend": {
    "type": "random",
    "countPerDay": 3,
    "timeRange": ["09:00", "22:00"]
  }
}
```

**Cron 任务：**
```
# 工作日
0 20 * * 1-5

# 周末（每天随机生成）
```

---

## 8. 错误处理

### 8.1 无法识别

**用户输入：** "随便吧"

**处理：**
```json
{
  "type": "random",
  "countPerDay": 3,
  "timeRange": ["09:00", "22:00"]
}
```

**提示用户：**
```
无法识别你的输入，使用默认设置：每天随机3次（09:00-22:00）。

确认吗？

1. 确认
2. 重新设置
```

### 8.2 冲突处理

**用户输入：** "早上9点，随机3次"

**处理：**
- 优先使用固定时间
- 提示用户确认

```
你既说了固定时间，又说了随机，我理解为：每天早上9点固定刷推。

对吗？

1. 对
2. 不对，重新设置
```

---

*文档版本: 1.0*
*更新: 2026-03-02*


path: docs/cron-jobs.md
content: # 手动提醒与维护

## 概述

本地安全版 `x-engagement` **默认不安装任何持久化调度任务**。

原因：
- 不直接修改用户 `crontab`
- 不直接创建后台常驻自动互动任务
- 所有维护和提醒都改成手动执行或由用户自行配置外部提醒工具

---

## 1. 每日热点总结

建议方式：
- 由用户在自己的日历、提醒事项、Notion、飞书任务里手动创建提醒
- 到点后由用户主动触发 skill

建议提醒文案：

```text
x-engagement: 查看 X Creator Inspiration 热点，更新 memory/daily/hotspots/tables/YYYY-MM-DD.md
```

---

## 2. 记忆清理

### 2.1 预览

```bash
cd /path/to/x-engagement
./scripts/cleanup-memory.sh
```

### 2.2 实际删除

```bash
cd /path/to/x-engagement
./scripts/cleanup-memory.sh --apply
```

说明：
- 默认只预览，不删除
- 只处理 `~/memory/daily/hotspots` 子树
- 遇到 symlink 或路径异常会拒绝执行

---

## 3. 手动提醒模板

生成模板：

```bash
./scripts/setup-cron.sh
```

这个脚本现在只会生成：

```text
runtime/manual-reminders.txt
```

它不会：
- 写入 `crontab`
- 调用任何后台调度器
- 自动注册 X 互动任务

---

## 4. 状态检查

```bash
./scripts/check-cron.sh
```

当前作用：
- 检查手动提醒模板是否存在
- 检查记忆目录是否存在
- 提示当前模式是“无自动调度”

---

## 5. 如果以后仍想要调度

建议原则：
1. 先审查将执行的命令
2. 先备份现有调度配置
3. 只创建提醒，不创建自动发帖/自动评论任务
4. 不要让调度器绕过人工确认

推荐：
- 用系统提醒类工具提醒“该去执行”
- 不要让调度器直接驱动浏览器去点赞/评论/关注

---

## 6. 安全边界

这个 skill 的安全边界现在是：
- Browser Relay 用于受控浏览器操作
- 记忆目录只在 `~/memory/daily/hotspots`
- 维护任务必须人工触发
- 评论、点赞、关注必须人工确认


path: docs/browser-operations.md
content: # 浏览器操作模块

## 设计原则

基于本地 `browser-relay-cli`，目标是 **稳定、可审计、手动确认**：

- 使用你们自己的 Browser Relay 运行时，而不是 OpenClaw
- 优先 DOM 可见元素与标准输入，不依赖 stealth / 规避检测逻辑
- 点赞、关注、评论都先生成建议，再由用户确认是否执行
- 能复用现有 tab 就复用，不盲目开新窗口

---

## 1. 运行时准备

### 1.1 检查 Browser Relay

```bash
npx browser-relay-cli version
npx browser-relay-cli extension-path
npx browser-relay-cli relay-start
npx browser-relay-cli status
```

要求：
- `extensionConnected: true`
- Chrome/Chromium 已登录目标 X 账号

### 1.2 推荐流程

```bash
npx browser-relay-cli list-tabs
npx browser-relay-cli create-tab https://x.com/home
```

如果已经有可复用的 X tab，优先复用，不重复创建。

---

## 2. 核心规则

1. **不要自动发送评论。**
2. **不要自动安装调度器。**
3. **所有写操作先展示预览，再等待用户确认。**
4. **只在用户明确同意时执行点赞、关注、评论。**

---

## 3. 常用操作

### 3.1 打开首页

```bash
npx browser-relay-cli create-tab https://x.com/home
```

### 3.2 查看当前页面

```bash
npx browser-relay-cli screenshot <tabId>
npx browser-relay-cli describe-visible <tabId>
```

### 3.3 切到 Following / Recent

优先用 DOM 选择器；选择器失效时，再用截图 + 坐标点击。

```bash
npx browser-relay-cli click <tabId> 'a[href="/home"]'
npx browser-relay-cli click <tabId> 'a[href="/home?tab=following"]'
npx browser-relay-cli click <tabId> 'a[href="/home?tab=recent"]'
```

### 3.4 读取推文内容

```bash
npx browser-relay-cli raw CDP.send "{\"tabId\":<tabId>,\"method\":\"Runtime.evaluate\",\"params\":{\"expression\":\"(() => { const tweets = [...document.querySelectorAll('[data-testid=\\\"tweet\\\"]')]; return tweets.slice(0, 5).map((tweet) => ({ author: tweet.querySelector('[data-testid=\\\"User-Name\\\"]')?.innerText || '', content: tweet.querySelector('[data-testid=\\\"tweetText\\\"]')?.innerText || '', link: tweet.querySelector('a[href*=\\\"/status/\\\"]')?.href || '' })); })()\",\"returnByValue\":true}}"
```

---

## 4. 评论流程

### 4.1 生成评论建议

先基于 persona、历史记录、推文内容生成 1-3 个候选评论，只展示，不自动提交。

输出格式建议：

```text
推文作者: @example
推文链接: https://x.com/...
候选评论:
1. ...
2. ...
3. ...
```

### 4.2 用户确认后再填入输入框

```bash
npx browser-relay-cli click <tabId> '[data-testid="reply"]'
npx browser-relay-cli type <tabId> '[data-testid="tweetTextarea_0"]' '这里填入用户已确认的评论'
```

### 4.3 发送前最后确认

发送动作必须单独确认一次。确认前可以：

```bash
npx browser-relay-cli screenshot <tabId>
npx browser-relay-cli describe-visible <tabId>
```

确认后再执行：

```bash
npx browser-relay-cli click <tabId> '[data-testid="tweetButton"]'
```

---

## 5. 点赞与关注

点赞、关注同样走确认流：

1. 先展示目标账号/推文
2. 说明为什么要点赞或关注
3. 等用户确认
4. 再执行 click

示例：

```bash
npx browser-relay-cli click <tabId> '[data-testid="like"]'
npx browser-relay-cli click <tabId> '[data-testid$="-follow"]'
```

---

## 6. 频率建议

这些是人工运营建议，不是自动化节流器：

- 点赞：每小时不超过 20 次
- 评论：每小时不超过 5 次
- 关注：每小时不超过 5 次
- 评论之间保留自然阅读和思考时间

---

## 7. 故障处理

### 7.1 选择器失效

1. `screenshot`
2. `describe-visible`
3. `click-at` 或 `click-at-norm`

### 7.2 扩展未连接

```bash
npx browser-relay-cli status
```

如果未连接：
1. 运行 `npx browser-relay-cli relay-start`
2. 在 `chrome://extensions` 加载 unpacked 扩展
3. 重试 `status`

---

## 8. 与 x-engagement 整合

- Persona 仍然从 `memory/daily/hotspots/personas/*.md` 读取
- 评论历史仍然写到 `memory/daily/hotspots/history/comments/`
- Browser Relay 只负责浏览器交互，不负责定时任务

---

## 9. 最佳实践

1. 复用已有 tab
2. 所有写操作先预览
3. 评论发送前二次确认
4. 定期手动运行清理脚本，而不是后台常驻
5. 把 Browser Relay 当成受控工具，不要当隐身机器人


path: docs/memory-system.md
content: # 记忆系统设计

## 概述

**目的：**
- 记录每次评论内容
- 记录用户说过的话
- 评论前检查历史，避免矛盾
- 学习用户习惯和偏好

**核心原则：**
- 事事记录
- 评论前检查
- 避免自相矛盾

---

## 1. 记忆层结构

```
memory/daily/hotspots/
├── .onboarding_complete     # Onboarding 完成标记
├── .config.json             # 用户配置
│
├── personas/
│   └── [handle].md          # Persona 描述
│
├── events/                  # 重大事件（永久保留）
│   └── iran-war-2026.md
│
├── tables/                  # 每日热点（滚动7天）
│   ├── 2026-03-01.md
│   └── 2026-03-02.md
│
└── history/                 # 历史记录
    ├── comments/            # 评论历史
    │   ├── 2026-03-01.md
    │   └── 2026-03-02.md
    │
    ├── daily/               # 每日日志
    │   ├── 2026-03-01.md
    │   └── 2026-03-02.md
    │
    └── user-facts/          # 用户事实（永久保留）
        ├── preferences.md   # 偏好
        └── events.md        # 事件（如"昨天出去吃饭了"）
```

---

## 2. 评论历史记录

### 2.1 为什么记录

**问题场景：**
```
Day 1: 用户说"昨天出去吃饭了"
Day 2: Bot 评论"你最近都在家做饭啊"
→ 自相矛盾！
```

**解决方案：**
- 记录每次评论内容
- 记录用户说过的话
- 评论前检查历史

### 2.2 记录格式

**文件：** `memory/daily/hotspots/history/comments/YYYY-MM-DD.md`

**格式：**

```markdown
# 2026-03-01 评论历史

## 评论 1
- 时间: 10:23
- 对象: @user1
- 推文: "今天市场大涨！"
- 我的评论: "确实，趋势起来了。"
- 语言: 中文

## 评论 2
- 时间: 10:29
- 对象: @user2
- 推文: "AI will replace all jobs"
- 我的评论: "Facts. Adapt or get left behind."
- 语言: 英文

## 评论 3
- 时间: 10:35
- 对象: @user3
- 推文: "昨天出去吃饭了，好吃"
- 我的评论: "哪家店？"
- 用户回复: "海底捞"
- 语言: 中文

---

*总计: 3 条评论*
```

### 2.3 记录时机

**每次评论后立即记录：**

```python
def record_comment(comment_data):
    today = get_today_date()
    file = f"memory/daily/hotspots/history/comments/{today}.md"
    
    # 追加到文件
    append_to_file(file, format_comment(comment_data))
```

### 2.4 记录内容

**必记：**
- 评论时间
- 评论对象（@handle）
- 原推文内容（简短摘要）
- 我的评论内容
- 语言

**可选：**
- 用户回复
- 点赞数
- 回复数

---

## 3. 用户事实记录

### 3.1 为什么记录

**用户说过的话很重要：**
- "我昨天出去吃饭了" → 记住
- "我不喜欢政治" → 记住
- "我最近在看 AI 项目" → 记住

**避免矛盾：**
- 不要评论"你最近都在家"
- 不要推荐政治内容
- 多推荐 AI 相关

### 3.2 记录格式

**文件：** `memory/daily/hotspots/history/user-facts/events.md`

**格式：**

```markdown
# 用户事件记录

## 2026-03-01
- 用户说: "昨天出去吃饭了，海底捞"
  - 推断: 用户喜欢吃火锅，偶尔外出就餐
  - 记录时间: 2026-03-01 10:35

- 用户说: "最近在看 AI 项目"
  - 推断: 对 AI 感兴趣
  - 记录时间: 2026-03-01 14:22

## 2026-02-28
- 用户说: "我不喜欢政治"
  - 推断: 避免政治话题
  - 记录时间: 2026-02-28 09:15
```

### 3.3 用户偏好

**文件：** `memory/daily/hotspots/history/user-facts/preferences.md`

**格式：**

```markdown
# 用户偏好

## 话题偏好
- 喜欢: AI, crypto, tech, 美食
- 避免: 政治, 战争

## 语言偏好
- 主要: 中文
- 次要: 英文

## 评论风格偏好
- 短、有力、用梗
- 不用 emoji

## 时间偏好
- 活跃时段: 20:00-23:00
- 避免时段: 02:00-08:00

## 其他
- 不喜欢被纠正
- 喜欢直接表达
```

### 3.4 记录时机

**识别用户陈述：**

```python
def detect_user_fact(message):
    patterns = [
        r"我(昨天|今天|最近).*",  # "我昨天出去吃饭了"
        r"我(喜欢|不喜欢|爱|恨).*",  # "我喜欢AI"
        r"我(在|正在|去).*",  # "我在看AI项目"
    ]
    
    for pattern in patterns:
        if match(pattern, message):
            return extract_fact(message)
    
    return None

# 使用
fact = detect_user_fact("我昨天出去吃饭了")
if fact:
    record_user_fact(fact)
```

---

## 4. 评论前检查

### 4.1 检查流程

```
准备评论 → 检查历史 → 检查用户事实 → 确认不矛盾 → 发表评论
```

### 4.2 检查内容

**1. 评论历史：**
- 最近7天对同一用户说过什么
- 避免重复评论
- 避免自相矛盾

**2. 用户事实：**
- 用户说过什么
- 用户偏好是什么
- 避免触雷

### 4.3 实现代码

```python
def check_before_comment(tweet, proposed_comment):
    issues = []
    
    # 1. 检查评论历史
    recent_comments = load_recent_comments(days=7)
    
    # 避免重复
    for c in recent_comments:
        if c.target == tweet.author:
            if similarity(c.text, proposed_comment) > 0.8:
                issues.append(f"最近对 @{tweet.author} 说过类似的话")
    
    # 避免矛盾
    for c in recent_comments:
        if contradicts(c.text, proposed_comment):
            issues.append(f"与之前的评论矛盾: '{c.text}'")
    
    # 2. 检查用户事实
    user_facts = load_user_facts()
    
    # 检查偏好
    if contains_political_content(proposed_comment):
        if "政治" in user_facts.avoid_topics:
            issues.append("用户不喜欢政治内容")
    
    # 检查事件矛盾
    if "在家" in proposed_comment and user_facts.has_event("昨天出去吃饭了"):
        issues.append("用户昨天出去吃饭了，说'在家'会矛盾")
    
    return issues
```

### 4.4 矛盾检测示例

**场景：**

```python
# 用户说过
user_fact = "昨天出去吃饭了"

# 准备评论
proposed_comment = "你最近都在家做饭啊"

# 检测矛盾
def is_contradiction(fact, comment):
    keywords_pairs = [
        (["出去", "外出", "餐厅"], ["在家", "家里", "自己做"]),
        (["喜欢", "爱"], ["不喜欢", "讨厌"]),
        (["买了", "入手"], ["没钱", "穷"]),
    ]
    
    for pos, neg in keywords_pairs:
        if any(p in fact for p in pos) and any(n in comment for n in neg):
            return True
    
    return False

# 结果: True → 矛盾 → 拒绝评论
```

---

## 5. 每日日志

### 5.1 记录内容

**文件：** `memory/daily/hotspots/history/daily/YYYY-MM-DD.md`

**格式：**

```markdown
# 2026-03-01 每日日志

## 刷推统计
- 总时长: 30分钟
- For You 浏览: 20屏
- Following 点赞: 15条
- Following 评论: 5条
- 新关注: 3人

## 评论记录
1. @user1: "确实，趋势起来了。"
2. @user2: "Facts."
3. @user3: "哪家店？"
4. @user4: "This is the way."
5. @user5: "妙啊"

## 用户互动
- 用户说: "昨天出去吃饭了"
- 用户说: "最近在看AI项目"

## 重大事件
- 伊朗战争爆发（记录到 events/iran-war-2026.md）

## 备注
- 用户今天心情不错
- 避免政治话题

---

*生成时间: 2026-03-01 23:00*
```

### 5.2 日志用途

- 回顾每日活动
- 分析刷推效果
- 调整策略
- 记忆持久化

---

## 6. 记忆清理策略

### 6.1 滚动删除

| 类型 | 保留时间 | 说明 |
|------|----------|------|
| 评论历史 | 30天 | 超过30天删除 |
| 每日日志 | 30天 | 超过30天删除 |
| 热点表格 | 7天 | 超过7天删除 |
| 用户事实 | 永久 | 一直保留 |
| 重大事件 | 永久 | 一直保留 |
| Persona | 永久 | 一直保留 |

### 6.2 清理脚本

```bash
# 每周运行一次
find memory/daily/hotspots/history/comments -mtime +30 -delete
find memory/daily/hotspots/history/daily -mtime +30 -delete
find memory/daily/hotspots/tables -mtime +7 -delete
```

---

## 7. 记忆检索

### 7.1 检索场景

**评论前：**
- 检索最近7天评论历史
- 检索用户事实

**生成 persona：**
- 检索用户偏好
- 检索历史评论风格

**每日总结：**
- 检索当日日志
- 检索当日评论

### 7.2 检索代码

```python
def retrieve_memory(query_type, **kwargs):
    if query_type == "recent_comments":
        days = kwargs.get("days", 7)
        return load_comments_from_last_n_days(days)
    
    elif query_type == "user_facts":
        return load_user_facts()
    
    elif query_type == "daily_log":
        date = kwargs.get("date", today())
        return load_daily_log(date)
    
    elif query_type == "persona":
        handle = kwargs.get("handle")
        return load_persona(handle)
```

---

## 8. 完整流程示例

### 8.1 评论流程（带记忆检查）

```python
def comment_with_memory_check(tweet, persona):
    # 1. 准备评论
    proposed_comment = generate_comment(tweet, persona)
    
    # 2. 检查历史
    issues = check_before_comment(tweet, proposed_comment)
    
    # 3. 如果有问题，重新生成
    if issues:
        print(f"问题: {issues}")
        proposed_comment = regenerate_comment(tweet, persona, avoid=issues)
    
    # 4. 发表评论
    post_comment(proposed_comment)
    
    # 5. 记录到历史
    record_comment({
        "time": now(),
        "target": tweet.author,
        "tweet": tweet.text,
        "comment": proposed_comment,
        "language": detect_language(proposed_comment)
    })
    
    return proposed_comment
```

### 8.2 用户说了一句话（记录）

```python
def handle_user_message(message):
    # 1. 检测是否是事实陈述
    fact = detect_user_fact(message)
    
    # 2. 如果是，记录
    if fact:
        record_user_fact(fact)
        print(f"记录用户事实: {fact}")
    
    # 3. 检测偏好
    preference = detect_preference(message)
    if preference:
        update_user_preference(preference)
        print(f"更新用户偏好: {preference}")
```

---

## 9. 记忆系统检查清单

```
□ 评论历史目录已创建
□ 每日日志目录已创建
□ 用户事实目录已创建
□ 评论前检查逻辑已实现
□ 矛盾检测已实现
□ 记录函数已实现
□ 清理脚本已设置
```

---

*文档版本: 1.0*
*更新: 2026-03-02*


path: docs/comment-generation.md
content: # 评论生成逻辑

## 概述

**目标：**
- 生成符合 persona 风格的评论
- 匹配推文语言
- 避免自相矛盾
- 短、有力、有趣

---

## 1. 评论流程

```
推文 → 检查 → 生成 → 检查历史 → 检查用户事实 → 发表 → 记录
```

### 1.1 完整流程

```python
def comment_workflow(tweet):
    # 1. 检查推文
    if not should_comment(tweet):
        return None
    
    # 2. 生成评论
    persona = load_persona(config.persona)
    proposed = generate_comment(tweet, persona)
    
    # 3. 检查历史
    issues = check_history(tweet, proposed)
    if issues:
        proposed = regenerate(proposed, avoid=issues)
    
    # 4. 检查用户事实
    issues = check_user_facts(proposed)
    if issues:
        proposed = regenerate(proposed, avoid=issues)
    
    # 5. 发表
    post_comment(proposed)
    
    # 6. 记录
    record_comment(tweet, proposed)
    
    return proposed
```

---

## 2. 推文检查

### 2.1 跳过条件

| 条件 | 说明 |
|------|------|
| 发布时间 > 2小时 | 只评论新推文 |
| 政治/战争内容 | 避免敏感话题 |
| 已评论过 | 避免重复 |
| 推文语言不匹配 | 无法用正确语言评论 |

### 2.2 检查代码

```python
def should_comment(tweet):
    # 1. 检查时间
    age_hours = (now() - tweet.timestamp) / 3600
    if age_hours > 2:
        return False
    
    # 2. 检查类型
    if is_political(tweet.text) or is_war(tweet.text):
        return False
    
    # 3. 检查是否已评论
    history = load_recent_comments(days=7)
    if any(c.target == tweet.author and c.tweet == tweet.text for c in history):
        return False
    
    # 4. 检查语言
    lang = detect_language(tweet.text)
    if lang not in ["zh", "en"]:
        return False
    
    return True
```

### 2.3 政治检测

```python
political_keywords = [
    "特朗普", "拜登", "习近平", "普京",
    "Trump", "Biden", "Putin", "Xi",
    "选举", "election", "投票", "vote",
    "民主党", "共和党", "Democrat", "Republican"
]

war_keywords = [
    "战争", "war", "导弹", "missile",
    "轰炸", "bomb", "伊朗", "Iran",
    "以色列", "Israel", "加沙", "Gaza"
]

def is_political(text):
    return any(kw in text for kw in political_keywords)

def is_war(text):
    return any(kw in text for kw in war_keywords)
```

---

## 3. 语言匹配

### 3.1 语言检测

```python
def detect_language(text):
    # 简单检测
    chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
    total_chars = len(text)
    
    if chinese_chars / total_chars > 0.3:
        return "zh"
    else:
        return "en"
```

### 3.2 语言匹配规则

| 推文语言 | 评论语言 | 说明 |
|----------|----------|------|
| 中文 | 中文 | 中文评论 |
| 英文 | 英文 | 英文评论 |
| 混合 | 英文 | 默认英文 |
| 其他 | 跳过 | 不评论 |

---

## 4. Persona 风格应用

### 4.1 Persona 内容

**从 `memory/daily/hotspots/personas/[handle].md` 读取：**

```markdown
## 评论风格
- 长度: 1句话
- 特点: 短、有力、用梗
- 示例:
  1. "Facts."
  2. "This is the way."
  3. "Real talk."
```

### 4.2 风格应用

```python
def apply_persona_style(comment, persona):
    # 1. 检查长度
    if len(comment) > persona.max_comment_length:
        comment = shorten(comment, persona.max_comment_length)
    
    # 2. 检查语气
    if persona.tone == "casual":
        comment = make_casual(comment)
    
    # 3. 添加特色
    if persona.use_slang:
        comment = add_slang(comment, persona.common_words)
    
    return comment
```

### 4.3 风格示例

**Persona 1: 短、有力、用梗**

```
推文: "AI will replace all jobs"
评论: "Facts. Adapt or get left behind."
```

**Persona 2: 幽默、自嘲**

```
推文: "今天亏了50%"
评论: "韭菜互帮互助，我昨天亏了60%"
```

**Persona 3: 正式、分析**

```
推文: "市场分析"
评论: "分析到位。补充一点：..."
```

---

## 5. 评论模板

### 5.1 中文模板

**认同类：**
- "确实"
- "有道理"
- "妙啊"
- "太真实了"
- "学到了"

**幽默类：**
- "韭菜互帮互助"
- "扎心了老铁"
- "笑死"
- "真实得让人害怕"

**梗类：**
- "这波啊，这波是..."
- "好家伙"
- "绝了"
- "妙啊妙啊"

**提问类：**
- "哪家店？"
- "什么项目？"
- "怎么做到的？"

### 5.2 英文模板

**Agreement:**
- "Facts."
- "This."
- "Based."
- "Real talk."
- "This is the way."

**Humor:**
- "Lol"
- "Dead"
- "Bro really said that"

**Crypto/Tech:**
- "Wen moon?"
- "Diamond hands"
- "WAGMI"
- "Probably nothing"

**Question:**
- "Source?"
- "Proof?"
- "How?"

---

## 6. 历史检查

### 6.1 检查内容

**1. 重复检查：**
- 最近7天对同一用户说过类似的话吗？
- 相似度 > 80% → 重新生成

**2. 矛盾检查：**
- 与之前的评论矛盾吗？
- 矛盾 → 重新生成

### 6.2 实现代码

```python
def check_history(tweet, proposed):
    issues = []
    
    # 加载最近评论
    recent = load_comments_from_last_n_days(7)
    
    # 检查重复
    for c in recent:
        if c.target == tweet.author:
            if similarity(c.text, proposed) > 0.8:
                issues.append({
                    "type": "duplicate",
                    "previous": c.text
                })
    
    # 检查矛盾
    for c in recent:
        if contradicts(c.text, proposed):
            issues.append({
                "type": "contradiction",
                "previous": c.text
            })
    
    return issues
```

### 6.3 相似度计算

```python
def similarity(text1, text2):
    # Jaccard 相似度
    words1 = set(text1.lower().split())
    words2 = set(text2.lower().split())
    
    intersection = words1 & words2
    union = words1 | words2
    
    return len(intersection) / len(union)
```

---

## 7. 用户事实检查

### 7.1 检查内容

**从 `memory/daily/hotspots/history/user-facts/` 读取：**

- 用户说过的话
- 用户偏好
- 避免触雷

### 7.2 实现代码

```python
def check_user_facts(proposed):
    issues = []
    
    facts = load_user_facts()
    
    # 检查偏好
    if contains_political_content(proposed):
        if "政治" in facts.avoid_topics:
            issues.append({
                "type": "preference_violation",
                "topic": "政治"
            })
    
    # 检查事件矛盾
    for event in facts.events:
        if event_contradicts(event, proposed):
            issues.append({
                "type": "event_contradiction",
                "event": event.text
            })
    
    return issues
```

### 7.3 矛盾示例

```python
# 用户说过
event = "昨天出去吃饭了"

# 准备评论
proposed = "你最近都在家做饭啊"

# 检测
def event_contradicts(event, comment):
    pairs = [
        (["出去", "外出", "餐厅"], ["在家", "家里"]),
        (["喜欢"], ["不喜欢"]),
        (["买了"], ["没钱"])
    ]
    
    for pos, neg in pairs:
        if any(p in event for p in pos) and any(n in comment for n in neg):
            return True
    
    return False

# 结果: True → 矛盾 → 重新生成
```

---

## 8. 重新生成

### 8.1 重新生成逻辑

```python
def regenerate(tweet, persona, avoid_issues):
    # 根据 issue 类型调整
    for issue in avoid_issues:
        if issue["type"] == "duplicate":
            # 换一种说法
            return generate_different_style(tweet, persona)
        
        elif issue["type"] == "contradiction":
            # 避免矛盾内容
            return generate_avoiding(tweet, persona, issue["previous"])
        
        elif issue["type"] == "preference_violation":
            # 避免偏好违规
            return generate_neutral(tweet, persona)
        
        elif issue["type"] == "event_contradiction":
            # 避免事件矛盾
            return generate_avoiding(tweet, persona, issue["event"])
    
    # 默认重新生成
    return generate_comment(tweet, persona)
```

### 8.2 换一种说法

```python
def generate_different_style(tweet, persona):
    # 尝试不同风格
    styles = ["agreement", "humor", "question", "neutral"]
    
    for style in styles:
        comment = generate_with_style(tweet, persona, style)
        if is_acceptable(comment):
            return comment
    
    # 如果都不行，用简单认同
    return "确实" if detect_language(tweet.text) == "zh" else "Facts."
```

---

## 9. 完整示例

### 9.1 示例 1：正常评论

```
推文: @user1 "今天市场大涨！"
语言: 中文
时间: 1小时前
类型: crypto

流程:
1. ✓ 时间检查通过（< 2小时）
2. ✓ 类型检查通过（非政治）
3. ✓ 历史检查通过（未评论过）
4. 生成评论: "确实，趋势起来了。"
5. ✓ 用户事实检查通过
6. 发表
7. 记录

结果: "确实，趋势起来了。"
```

### 9.2 示例 2：重复评论

```
推文: @user1 "市场分析"
语言: 中文
时间: 30分钟前

历史:
- 昨天 @user1 "市场走势" → "确实，分析到位。"

流程:
1. ✓ 时间检查通过
2. ✓ 类型检查通过
3. ✗ 历史检查：重复（相似度 0.85）
4. 重新生成: "这波分析可以"
5. ✓ 用户事实检查通过
6. 发表
7. 记录

结果: "这波分析可以"
```

### 9.3 示例 3：矛盾评论

```
推文: @user2 "最近都在家做饭"
语言: 中文
时间: 1小时前

用户事实:
- 昨天: "出去吃饭了，海底捞"

流程:
1. ✓ 时间检查通过
2. ✓ 类型检查通过
3. ✓ 历史检查通过
4. 生成评论: "你最近都在家做饭啊"
5. ✗ 用户事实检查：矛盾（用户昨天出去吃饭）
6. 重新生成: "做饭不错，偶尔出去换换口味也好"
7. 发表
8. 记录

结果: "做饭不错，偶尔出去换换口味也好"
```

### 9.4 示例 4：政治内容

```
推文: @user3 "特朗普最新政策"
语言: 中文
时间: 1小时前

流程:
1. ✓ 时间检查通过
2. ✗ 类型检查：政治内容
3. 跳过

结果: 不评论
```

---

## 10. 评论质量检查

### 10.1 质量标准

| 标准 | 说明 |
|------|------|
| 长度 | 1-2句话 |
| 语言 | 匹配推文 |
| 语气 | 符合 persona |
| 内容 | 不矛盾、不重复 |
| 有趣 | lighthearted |

### 10.2 质量检查代码

```python
def quality_check(comment, persona):
    score = 0
    
    # 长度
    if 5 <= len(comment) <= 50:
        score += 20
    
    # 语言
    if matches_language(comment, persona.language):
        score += 20
    
    # 语气
    if matches_tone(comment, persona.tone):
        score += 20
    
    # 有趣
    if is_lighthearted(comment):
        score += 20
    
    # 不矛盾
    if not has_contradictions(comment):
        score += 20
    
    return score
```

---

*文档版本: 1.0*
*更新: 2026-03-02*


path: docs/human-behavior.md
content: # 自然节奏与确认规范

## 核心原则

这份文档不讨论规避检测，也不追求“伪装真人”。

目标只有两个：
- 让运营节奏自然，不要机械刷动作
- 把高风险写操作留给用户确认

---

## 1. 阅读与判断节奏

建议在以下动作之间保留真实的阅读时间：

- 看完推文再决定是否点赞
- 先读作者主页，再决定是否关注
- 先生成评论候选，再决定是否回复

建议范围：
- 短推文：1-3 秒
- 长推文 / 线程：5-15 秒
- 评论前额外思考：10-30 秒

---

## 2. 评论确认流程

评论必须分三步：

1. 读取推文与上下文
2. 生成候选评论
3. 用户确认后才填入并发送

推荐模板：

```text
候选评论:
1. ...
2. ...
3. ...

是否发送第 1 条？
```

---

## 3. 点赞与关注确认

点赞和关注也属于账号级写操作。

推荐流程：
1. 展示目标推文或主页
2. 说明为什么值得点赞或关注
3. 等用户明确同意
4. 再执行浏览器点击

---

## 4. 频率建议

这些是人工运营建议，不是自动调度规则：

| 操作 | 建议上限 |
|------|----------|
| 点赞 | 每小时 20 次以内 |
| 评论 | 每小时 5 次以内 |
| 关注 | 每小时 5 次以内 |

如果用户感觉账号敏感，应进一步降低频率。

---

## 5. 不要做的事

- 不要批量自动评论
- 不要自动循环滚动并持续互动
- 不要绕过用户确认直接发送
- 不要把 Browser Relay 当隐身机器人使用

---

## 6. 建议执行方式

- Browser Relay 负责浏览器控制
- x-engagement 负责 persona、记忆、候选评论
- 用户负责最后确认

这样可以保留效率，同时把风险压到最低


path: docs/comment-rules.md
content: # 评论规则（重要！）

## ⚠️ 绝对禁止的错误

### 1. 评论内容与推文无关

**❌ 错误：**
- 打开推文 A 的评论框
- 发送了针对推文 B 的评论
- 评论内容和推文不相关

**✓ 正确：**
- 评论前确认评论框对应的是哪条推文
- 再看一遍推文内容
- 确保评论和推文相关

**真实案例（2026-03-03）：**
```
推文：Tom Lee 看好 BTC/ETH
错误评论："终于来了，龙虾体验更好了"
（这个评论是给 Telegram 流式发送的，不相关）

正确评论："Tom Lee 还是稳"
```

**操作流程：**
```
1. 点击 Reply 打开评论框
2. 确认评论框显示的博主和推文内容
3. 再读一遍推文内容
4. 生成相关评论
5. 发送前再次确认
```

---

### 2. 错误的页面

**❌ 错误：**
- 在 Following 的 Popular 页面评论
- 在 For You 页面评论

**✓ 正确：**
- **只在 Following 的 Recent 页面评论**

**原因：**
- Popular 页面是热门推文，评论已经很多
- Recent 页面是最新推文，评论少，更自然
- For You 页面是算法推荐，不是关注的人

**操作流程：**
```
1. 打开 https://x.com/home
2. 点击 "Following" 标签
3. 确保是 "Recent"（不是 Popular）
4. 开始评论
```

---

### 2. 重复评论同一推文

**❌ 错误：**
- 在同一条推文下评论两次
- 在同一个博主的同一条推文下重复评论

**✓ 正确：**
- **每次评论前检查历史**
- 确保没有评论过这条推文
- 确保没有评论过这个博主

**检查流程：**
```
1. 读取 memory/daily/hotspots/history/comments/[日期].json
2. 检查是否已评论过这个博主
3. 检查是否已评论过这条推文
4. 如果已评论，跳过这条推文
```

**示例：**
```json
{
  "2026-03-02": [
    {
      "author": "@siantgirl",
      "tweet_id": "2028297844399153394",
      "content": "确实，小红书流量太少了。B站可以试试",
      "time": "2026-03-02 13:34:00"
    }
  ]
}
```

---

## 正确的评论流程

### Step 1: 打开正确的页面

```javascript
// 1. 打开首页
npx browser-relay-cli create-tab https://x.com/home

// 2. 切到 Following / Recent
npx browser-relay-cli click <tabId> 'a[href="/home?tab=following"]'
npx browser-relay-cli click <tabId> 'a[href="/home?tab=recent"]'
```

### Step 2: 读取评论历史

```javascript
// 读取今天的评论历史
const today = new Date().toISOString().split('T')[0]; // 2026-03-02
const historyFile = `memory/daily/hotspots/history/comments/${today}.json`;

let history = [];
try {
  history = JSON.parse(readFileSync(historyFile));
} catch (e) {
  // 文件不存在，创建新文件
  history = [];
}
```

### Step 3: 选择推文并检查

```javascript
npx browser-relay-cli raw CDP.send "{\"tabId\":<tabId>,\"method\":\"Runtime.evaluate\",\"params\":{\"expression\":\"(() => { const tweet = document.querySelectorAll('[data-testid=\\\"tweet\\\"]')[0]; if (!tweet) return null; const author = tweet.querySelector('[data-testid=\\\"User-Name\\\"]')?.innerText || ''; const tweetLink = tweet.querySelector('a[href*=\\\"/status/\\\"]'); const tweetId = tweetLink?.href.match(/status\\\\/(\\\\d+)/)?.[1] || ''; const content = tweet.querySelector('[data-testid=\\\"tweetText\\\"]')?.innerText || ''; return { author, tweetId, content }; })()\",\"returnByValue\":true}}"
```

### Step 4: 检查历史并评论

```javascript
// 检查是否已评论
const alreadyCommented = history.some(h => 
  h.author === tweet.author || 
  h.tweet_id === tweet.tweetId
);

if (alreadyCommented) {
  // 跳过这条推文
  continue;
}

// 生成评论
const comment = generateComment(tweet.content, persona);

// 发表评论
await postComment(tweet, comment);

// 记录到历史
history.push({
  author: tweet.author,
  tweet_id: tweet.tweetId,
  content: comment,
  time: new Date().toISOString()
});

writeFileSync(historyFile, JSON.stringify(history, null, 2));
```

---

## 检查清单

**每次评论前必须检查：**

- [ ] 页面是否正确（Following → Recent）
- [ ] 是否已评论过这个博主
- [ ] 是否已评论过这条推文
- [ ] 评论是否符合 persona
- [ ] 评论是否与推文相关

**如果任何一项是 "否"，则跳过这条推文。**

---

## 示例：完整的评论流程

```javascript
async function commentWithChecks() {
  // 1. 打开正确页面
  await openFollowingRecent();
  
  // 2. 读取历史
  const history = loadHistory();
  
  // 3. 遍历推文
  const tweets = await getTweets();
  
  for (const tweet of tweets) {
    // 4. 检查历史
    if (hasCommented(history, tweet)) {
      continue; // 跳过
    }
    
    // 5. 生成评论
    const comment = generateComment(tweet);
    
    // 6. 发表
    await postComment(tweet, comment);
    
    // 7. 记录
    saveToHistory(history, tweet, comment);
    
    // 8. 只评论一条
    break;
  }
}

function hasCommented(history, tweet) {
  return history.some(h => 
    h.author === tweet.author || 
    h.tweet_id === tweet.tweetId
  );
}
```

---

## 常见错误示例

### 错误 1: 在 Popular 页面评论

```
❌ 错误：
- 打开 Following → Popular
- 评论热门推文

✓ 正确：
- 打开 Following → Recent
- 评论最新推文
```

### 错误 2: 重复评论同一人

```
❌ 错误：
- 13:00 评论 @siantgirl 的推文 A："妙啊"
- 13:05 评论 @siantgirl 的推文 B："确实"

✓ 正确：
- 13:00 评论 @siantgirl 的推文 A："妙啊"
- 13:05 跳过 @siantgirl 的推文 B（已评论过这个博主）
- 13:10 评论 @other_user 的推文 C："Facts."
```

### 错误 3: 重复评论同一推文

```
❌ 错误：
- 13:00 评论推文 ID 123："妙啊"
- 13:05 评论推文 ID 123："确实"

✓ 正确：
- 13:00 评论推文 ID 123："妙啊"
- 13:05 跳过推文 ID 123（已评论）
- 13:10 评论推文 ID 456："Facts."
```

---

## 记忆文件结构

```
memory/daily/hotspots/history/
├── comments/
│   ├── 2026-03-01.json  # 每天的评论历史
│   ├── 2026-03-02.json
│   └── 2026-03-03.json
└── daily/
    └── 2026-03-02.md    # 每日日志
```

**comments/[日期].json 格式：**
```json
[
  {
    "author": "@siantgirl",
    "tweet_id": "2028297844399153394",
    "content": "确实，小红书流量太少了。B站可以试试",
    "time": "2026-03-02T13:34:00+08:00"
  },
  {
    "author": "@petergyang",
    "tweet_id": "2028335209347944624",
    "content": "Facts. Taste is human.",
    "time": "2026-03-02T13:02:00+08:00"
  }
]
```

---

## 总结

**三个绝对禁止的错误：**

1. ❌ 在 Following 的 Popular 页面评论
2. ❌ 重复评论同一个博主
3. ❌ 重复评论同一条推文

**正确的做法：**

1. ✓ 只在 Following 的 Recent 页面评论
2. ✓ 每次评论前检查历史
3. ✓ 记录每次评论到历史文件

**违反任何一条，立即停止并修正。**

---

*版本: 1.0.0*
*创建时间: 2026-03-02*
*原因: 防止重复评论错误*


path: README.md
content: # X 运营辅助 Skill v4.2

完整的 X/Twitter 运营辅助方案。默认走 Browser Relay + 人工确认，不安装后台持久任务。

Browser Relay 仓库：
`https://github.com/jasonCodeSpace/browser-relay`

## 特性

- ✅ 完整 Onboarding 流程
- ✅ Persona 学习（抓取100条推文）
- ✅ 自然节奏与确认（先建议，后执行）
- ✅ 记忆系统（评论历史、用户事实、避免矛盾）
- ✅ 手动提醒模板（不改 crontab）
- ✅ 智能评论生成（语言匹配、风格应用）
- ✅ Browser Relay 集成（使用自有运行时）

## 快速开始

### 1. 准备 Browser Relay 运行时

```bash
npx browser-relay-cli version
npx browser-relay-cli extension-path
npx browser-relay-cli relay-start
```

如果需要查看 Browser Relay 源码或本地开发，请使用仓库：
`https://github.com/jasonCodeSpace/browser-relay`

### 2. 首次运行

```
用户: 刷推
Bot: 开始 Onboarding...
```

### 3. 后续使用

```
用户: 刷推半小时
Bot: 读取配置... 先给出互动建议，确认后执行...
```

## 文档

| 文档 | 说明 |
|------|------|
| [SKILL.md](SKILL.md) | 主入口 |
| [docs/onboarding.md](docs/onboarding.md) | Onboarding 流程 |
| [docs/human-behavior.md](docs/human-behavior.md) | 自然节奏与确认 |
| [docs/memory-system.md](docs/memory-system.md) | 记忆系统 |
| [docs/cron-jobs.md](docs/cron-jobs.md) | 手动提醒与维护 |
| [docs/comment-generation.md](docs/comment-generation.md) | 评论生成 |

## 目录结构

```
x-engagement/
├── SKILL.md                    # 主入口
├── README.md                   # 说明文档
├── docs/                       # 详细文档
│   ├── onboarding.md
│   ├── human-behavior.md
│   ├── memory-system.md
│   ├── cron-jobs.md
│   └── comment-generation.md
├── templates/                  # 模板文件
│   ├── persona.md
│   ├── config.json
│   └── daily-log.md
└── scripts/                    # 脚本
    ├── setup-cron.sh
    ├── check-cron.sh
    └── cleanup-memory.sh
```

## 核心功能

### 1. Onboarding

首次运行自动引导：
- 浏览器连接 + 登录检查
- 选择 Persona（自己或其他账号）
- 学习 Persona（抓取100条）
- 配置刷推习惯

### 2. 自然节奏与确认

保留自然阅读节奏，但不做规避检测设计：
- 评论前给出候选内容
- 发送前必须再次确认
- 点赞 / 关注也建议确认
- 保留频率建议

### 3. 记忆系统

三层记忆：
- 评论历史（避免重复、矛盾）
- 用户事实（记住用户说过的话）
- 每日日志（活动记录）

### 4. 手动维护

- 手动热点总结
- 手动记忆清理预览
- 手动记忆清理执行（`--apply`）
- 手动提醒模板生成

默认不直接安装 cron，也不修改用户 `crontab`。

### 5. 智能评论

- 语言匹配（中文/英文）
- Persona 风格应用
- 历史检查（避免矛盾）
- 用户事实检查

## 使用示例

### 示例 1：正常评论

```
推文: "今天市场大涨！"
评论: "确实，趋势起来了。"
```

### 示例 2：避免矛盾

```
用户昨天说: "出去吃饭了"
推文: "最近都在家做饭"
评论: "做饭不错，偶尔出去换换口味也好"（避免说"你都在家"）
```

### 示例 3：跳过政治

```
推文: "特朗普最新政策"
结果: 跳过（政治内容）
```

## 配置

### 刷推习惯

```json
{
  "forYou": {
    "followNewAccounts": true,
    "followCriteria": ["views_1m+", "crypto", "ai"]
  },
  "following": {
    "commentWithin": "2h",
    "avoidTopics": ["politics", "war"]
  }
}
```

### 频率限制

| 操作 | 每小时上限 |
|------|-----------|
| 关注 | 10 |
| 点赞 | 30 |
| 评论 | 10 |

## 故障排除

### 浏览器连接失败

```
1. 运行 `npx browser-relay-cli relay-start`
2. 在 Chrome 加载 Browser Relay 扩展
3. 运行 `npx browser-relay-cli status`
```

### 不希望自动发送评论

```
1. 保持默认确认流
2. 先生成候选评论
3. 用户确认后再点发送
```

### 记忆系统不工作

```
1. 检查目录是否存在: memory/daily/hotspots/
2. 检查文件权限
3. 运行: ./scripts/check-cron.sh
```

## 更新日志

### v4.2.0-local (2026-04-14)
- 移除自动 cron / crontab 修改
- 清理脚本改成默认 dry-run
- Browser Relay 替代 OpenClaw
- 评论改成先建议后确认

### v4.0.0 (2026-03-02)
- 结构化设计
- 记忆系统（评论历史、用户事实）
- 定时任务（每日热点）
- 人类行为模拟规范
- 智能评论生成

### v3.0.0 (2026-03-01)
- 完整 Onboarding 流程
- Persona 学习

### v2.0.0 (2026-02-28)
- 人类行为模拟
- 频率限制

### v1.0.0 (2026-02-27)
- 初始版本

## 许可证

MIT

---

*版本: 4.0.0*
*更新: 2026-03-02*


path: scripts/setup-cron.sh
content: #!/bin/bash
set -euo pipefail

# Prepare manual reminder templates without modifying system schedulers.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
RUNTIME_DIR="${SKILL_ROOT}/runtime"
MANUAL_FILE="${RUNTIME_DIR}/manual-reminders.txt"

mkdir -p "${RUNTIME_DIR}"

cat > "${MANUAL_FILE}" <<EOF
# x-engagement manual reminder templates
#
# This file is informational only.
# It does not modify crontab, launchd, or any other scheduler.
#
# Recommended manual routines:
#
# 1. Daily hotspot review
#    Run manually when needed:
#    cd "${SKILL_ROOT}" && ./scripts/check-cron.sh
#
# 2. Memory cleanup preview
#    cd "${SKILL_ROOT}" && ./scripts/cleanup-memory.sh
#
# 3. Memory cleanup apply
#    cd "${SKILL_ROOT}" && ./scripts/cleanup-memory.sh --apply
#
# If you later decide to create OS-level reminders, do it manually after review.
EOF

echo "=== x-engagement reminder setup ==="
echo "No persistent tasks were installed."
echo "Created manual reminder template:"
echo "  ${MANUAL_FILE}"
echo ""
echo "Review and run the listed commands manually."


path: scripts/daily-review.sh
content: #!/bin/bash

# x-engagement 每日复盘脚本
# 时间：每天晚上 22:00

TODAY=$(date +%Y-%m-%d)
DATA_FILE="data/engagement/${TODAY}.json"
PLAYBOOK="playbooks/comment-strategies.md"

echo "=== x-engagement 每日复盘 ==="
echo "日期: ${TODAY}"
echo ""

# 检查数据文件是否存在
if [ ! -f "$DATA_FILE" ]; then
    echo "⚠️ 今日无评论数据"
    exit 0
fi

# 读取今日数据
echo "📊 今日评论统计："
COMMENT_COUNT=$(jq 'length' "$DATA_FILE")
SUCCESS_COUNT=$(jq '[.[] | select(.result == "success")] | length' "$DATA_FILE")
ERROR_COUNT=$(jq '[.[] | select(.result == "error")] | length' "$DATA_FILE")

echo "- 总评论数: ${COMMENT_COUNT}"
echo "- 成功: ${SUCCESS_COUNT}"
echo "- 错误: ${ERROR_COUNT}"
echo ""

# 显示评论详情
echo "📝 评论详情："
jq -r '.[] | "- \(.time) @\(.author): \"\(.comment)\" [\(.result)]"' "$DATA_FILE"
echo ""

# 分析有效策略
echo "✅ 有效策略："
jq -r '.[] | select(.result == "success") | "- \"\(.comment)\" - \(.tweet_type) - \(.language)"' "$DATA_FILE"
echo ""

# 分析错误
if [ "$ERROR_COUNT" -gt 0 ]; then
    echo "❌ 错误分析："
    jq -r '.[] | select(.result == "error") | "- \"\(.comment)\" - \(.notes)"' "$DATA_FILE"
    echo ""
fi

# 生成建议
echo "💡 优化建议："
echo "- 查看 playbooks/comment-strategies.md 了解有效策略"
echo "- 查看 playbooks/changelog.md 了解最新变更"
echo "- 明天避免重复错误"

echo ""
echo "✓ 复盘完成"


path: scripts/cleanup-memory.sh
content: #!/bin/bash
set -euo pipefail

# Preview or apply cleanup inside the dedicated x-engagement memory subtree.

MODE="dry-run"
if [ "${1:-}" = "--apply" ]; then
  MODE="apply"
elif [ "${1:-}" != "" ]; then
  echo "Usage: $0 [--apply]"
  exit 1
fi

MEMORY_DIR="${HOME}/memory/daily/hotspots"
ALLOWED_PREFIX="${HOME}/memory/daily/hotspots"

case "${MEMORY_DIR}" in
  "${ALLOWED_PREFIX}"|"${ALLOWED_PREFIX}/"*) ;;
  *)
    echo "Refusing to run outside allowed memory path: ${ALLOWED_PREFIX}"
    exit 1
    ;;
esac

if [ -L "${MEMORY_DIR}" ]; then
  echo "Refusing to operate on symlinked memory directory: ${MEMORY_DIR}"
  exit 1
fi

if [ ! -d "${MEMORY_DIR}" ]; then
  echo "Memory directory does not exist: ${MEMORY_DIR}"
  exit 0
fi

echo "=== x-engagement memory cleanup (${MODE}) $(date) ==="

cleanup_dir() {
  local label="$1"
  local dir="$2"
  local days="$3"

  echo ""
  echo "${label}: ${dir} (older than ${days} days)"

  if [ ! -d "${dir}" ]; then
    echo "  skipped: directory missing"
    return 0
  fi

  mapfile -t matches < <(find "${dir}" -type f -mtime +"${days}" -print 2>/dev/null | sort)

  if [ "${#matches[@]}" -eq 0 ]; then
    echo "  nothing to clean"
    return 0
  fi

  printf '  %s\n' "${matches[@]}"

  if [ "${MODE}" = "apply" ]; then
    find "${dir}" -type f -mtime +"${days}" -delete 2>/dev/null
    echo "  deleted: ${#matches[@]} files"
  else
    echo "  preview only: rerun with --apply to delete"
  fi
}

cleanup_dir "评论历史" "${MEMORY_DIR}/history/comments" 30
cleanup_dir "每日日志" "${MEMORY_DIR}/history/daily" 30
cleanup_dir "热点表格" "${MEMORY_DIR}/tables" 7

echo ""
echo "Cleanup complete."


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)