# 社交与社区渠道

## Reddit

### 前置条件

- Reddit 账户和已注册的 [Reddit API 应用](https://www.reddit.com/prefs/apps)（script 类型）

Reddit 以 Python sidecar (`librefang.sidecar.adapters.reddit`, 随 `librefang-sdk` 发布) 形式运行。

### 设置步骤

1. 前往 [reddit.com/prefs/apps](https://www.reddit.com/prefs/apps) 创建一个新的 **script** 类型应用，记录应用名称下方的 Client ID 与 Client Secret。
2. 打开仪表板 Channels 页面选择 Reddit 并填表 —— 仪表板会把 `REDDIT_CLIENT_SECRET` 和 `REDDIT_PASSWORD` 写入 `~/.librefang/secrets.env`，把其余字段写入 `~/.librefang/config.toml` 的 `[sidecar_channels.env]` 表。等价的手工配置：

```toml
[[sidecar_channels]]
name = "reddit"
command = "python3"
args = ["-m", "librefang.sidecar.adapters.reddit"]
channel_type = "reddit"
default_agent = "assistant"
[sidecar_channels.env]
REDDIT_CLIENT_ID = "abc123"
REDDIT_USERNAME = "librefang-bot"
REDDIT_SUBREDDITS = "mysubreddit,rust"
REDDIT_USER_AGENT = "myorg/1.0 (by /u/myactual-reddit-name)"   # 必填，见下方
# REDDIT_ACCOUNT_ID = "prod"               # 可选，多 Bot 路由
# REDDIT_POLL_INTERVAL_SECS = "30"         # 可选，默认 30，下限 5
```

### 防封号 —— 上线前必读

Reddit 对 bot 行为有严格审查。adapter 自动做了能做的：

- **`REDDIT_USER_AGENT` 必填。** Reddit API 规则要求 UA 标明真实维护者（`by /u/<你的 Reddit 用户名>`）。占位默认值会在启动时被拒绝以避免 IP + 账号双封。请使用你自己的 Reddit 用户名。
- **默认 30 秒轮询间隔。** 低于 5 秒会快速烧掉每账号 60 req/min 的预算。下限是 5 秒，活跃度低的 sub 可以把 `REDDIT_POLL_INTERVAL_SECS` 调更高。
- **自动节流。** 当 Reddit 返回 `X-Ratelimit-Remaining < 10` 时，poller 会提前睡到 reset 窗口。Polling 收到 429 会退避；`/api/comment` 收到 429 会读取 `Retry-After` 重试一次。

adapter 无法强制、需要你自己保证的：

- Bot 账号至少 30 天 + 评论 karma > 100，否则容易被 shadowban。
- 进入任何 subreddit 前先跟 mod 报备。在陌生 sub 自动回复是常见的封号触发点。
- 通过 agent 配置里的 `group_trigger_patterns` 限制触发条件，让 bot 只回应 `/cmd` 或具名 @mention —— 对每条新评论都回复是最快的全站封号路径。

3. 把两个密钥写入 `~/.librefang/secrets.env`：

```bash
REDDIT_CLIENT_SECRET=your-client-secret
REDDIT_PASSWORD=your-reddit-password
```

4. 重启守护进程。

### 工作原理

Reddit sidecar 适配器使用 OAuth2（script 应用的 password grant）认证，按 5 秒间隔轮询每个已配置子版块的最新评论流，并通过 `POST /api/comment` 把回复以评论形式发出。Reddit 每个父项只允许一条回复，因此被分块的回复会以 `\n\n---\n\n` 拼接而不是拆成多条评论。

---

## Mastodon

### 前置条件

- 任意实例上的 Mastodon 账户
- 应用访问令牌（从实例的 **Preferences > Development** 页面生成，参见 [Mastodon API 文档](https://docs.joinmastodon.org/api/)）

Mastodon 以 Python sidecar (`librefang.sidecar.adapters.mastodon`, 随 `librefang-sdk` 发布) 形式运行。

### 设置步骤

1. 在 Mastodon 实例上，前往 **Preferences > Development > New Application**。
2. 授予 `read` 和 `write` 作用域。
3. 复制访问令牌（即 `MASTODON_ACCESS_TOKEN`）。
4. 在仪表盘 Channels 页面选择 Mastodon，填入实例 URL 与访问令牌——`MASTODON_ACCESS_TOKEN` 会写到 `~/.librefang/secrets.env`，`MASTODON_INSTANCE_URL` 写到 `~/.librefang/config.toml` 的 `[sidecar_channels.env]`。等价的手工配置：

```toml
[[sidecar_channels]]
name = "mastodon"
command = "python3"
args = ["-m", "librefang.sidecar.adapters.mastodon"]
channel_type = "mastodon"
[sidecar_channels.env]
MASTODON_INSTANCE_URL = "https://mastodon.social"
# MASTODON_VISIBILITY = "unlisted"          # public | unlisted | private | direct
# MASTODON_MAX_MESSAGE_LEN = "500"          # 实例若放宽限制可调高
# MASTODON_ACCOUNT_ID = "prod"              # 可选，多 bot 路由
```

5. 守护进程通过热加载自动起停 sidecar，无需手动重启。

### 工作原理

Mastodon sidecar 通过 Server-Sent Events (SSE) 连接到实例的用户流式 API 实时接收提及通知；SSE 失败时自动回退为 REST 轮询 `/api/v1/notifications`。出站回复通过 `POST /api/v1/statuses` 带 `in_reply_to_id` 链式发布，保证分片回复在客户端仍然是同一线程。

---

## Bluesky

### 前置条件

- Bluesky 账户（参见 [Bluesky API 文档](https://docs.bsky.app)）
- 应用密码（在 Bluesky **Settings > App Passwords** 中生成）

Bluesky 以 Python sidecar (`librefang.sidecar.adapters.bluesky`, 随 `librefang-sdk` 发布) 形式运行。

### 设置步骤

1. 在 Bluesky 中，前往 **Settings > App Passwords**，创建新的应用密码。
2. 添加 `[[sidecar_channels]]` 配置块：

```toml
[[sidecar_channels]]
name = "bluesky"
command = "python3"
args = ["-m", "librefang.sidecar.adapters.bluesky"]
channel_type = "bluesky"
[sidecar_channels.env]
BLUESKY_IDENTIFIER = "your-handle.bsky.social"
# BLUESKY_SERVICE_URL = "https://bsky.social"   # 自定义 PDS
# BLUESKY_ACCOUNT_ID = "prod"                   # 可选，多 bot 路由键
```

3. 通过仪表盘 Channels 页设置应用密码（自动写入 `~/.librefang/secrets.env` 的 `BLUESKY_APP_PASSWORD`），或直接放入 vault / `.env`。
4. 重启守护进程。

### 工作原理

sidecar 使用 `com.atproto.server.createSession` 认证，并在 JWT 过期前自动刷新。每 5 秒轮询 `app.bsky.notification.listNotifications`（仅过滤 `mention` + `reply` 通知），并通过 `com.atproto.repo.createRecord` 用 `app.bsky.feed.post` lexicon 发布回复。内存 LRU 缓存（容量 200）将入站通知 URI 映射到 `{root, parent}` 回复引用，从而把外发响应线程化关联到原帖子；分片回复时每个分片复用相同 reply ref，保证线程紧凑。

---

## Twitch

### 前置条件

- Twitch 账户和具有聊天权限的 OAuth 令牌（从 [Twitch 开发者门户](https://dev.twitch.tv/docs/irc/) 获取）

Twitch 由独立进程的 Python sidecar 适配器 (`librefang.sidecar.adapters.twitch`，随 `librefang-sdk` 一同发布) 提供。

### 设置步骤

1. 在 [Twitch Developer Console](https://dev.twitch.tv/console/apps) 注册应用。
2. 生成具有 `chat:read` 和 `chat:edit` 作用域的 OAuth 令牌（可使用 [twitchtokengenerator.com](https://twitchtokengenerator.com/) 等工具）。
3. 在 Dashboard 的 Channels 页面选择 Twitch 并填表 — Dashboard 会自动将 `TWITCH_OAUTH_TOKEN` 写入 `~/.librefang/secrets.env`，其余字段写入 `~/.librefang/config.toml` 的 `[sidecar_channels.env]` 表。等价的手工配置：

```toml
[[sidecar_channels]]
name = "twitch"
command = "python3"
args = ["-m", "librefang.sidecar.adapters.twitch"]
channel_type = "twitch"
default_agent = "assistant"
[sidecar_channels.env]
TWITCH_NICK = "librefang-bot"
TWITCH_CHANNELS = "channel1,channel2"      # 逗号分隔，不带 '#'
# TWITCH_ACCOUNT_ID = "prod"                # 可选，多机器人路由
# TWITCH_RATE_LIMIT_MSGS = "20"             # 非 mod 账号 20/30s；mod 可设为 100
# TWITCH_RATE_LIMIT_SECS = "30"
```

4. 将 OAuth 令牌写入 `~/.librefang/secrets.env`：

```bash
TWITCH_OAUTH_TOKEN=oauth:your-token
```

缺失 `oauth:` 前缀时会自动补齐。重启守护进程。

### 上线前必读 — 安全

- **默认启用 TLS。** Sidecar 通过 `ssl.create_default_context()` 连接 `irc.chat.twitch.tv:6697`。原 in-process Rust 适配器使用明文 6667，每次重连都会泄露 OAuth 令牌 — 升级即默认修复。
- **逐条消息回复线程。** Sidecar 请求 `twitch.tv/tags` 能力，每条入站 PRIVMSG 都带 `@id` 标签；出站回复附加 `@reply-parent-msg-id=<id>` 标签，Twitch 会以线程方式渲染机器人的回复。
- **发送速率限制。** Twitch 反垃圾会在非 mod 账号超过 20 msg / 30s 时踢出聊天室（mod 为 100 / 30s）。Sidecar 用进程内 token bucket 对每条 PRIVMSG 限流。mod 账号可通过 `TWITCH_RATE_LIMIT_MSGS=100` 放宽；多进程运营者仍应每个账号保持一个进程，因为限流是本地的。

### 工作原理

Twitch sidecar 适配器维持一个持久 TLS IRC 连接到 `irc.chat.twitch.tv:6697`，通过 `PASS oauth:<token>` / `NICK` 完成认证，请求 `twitch.tv/tags twitch.tv/commands` 能力后 JOIN 所有配置的频道。它解析入站 PRIVMSG 帧（包括 IRCv3 `@`-tag 块），把 `@id` 标签作为 `thread_id` 透出以便回复时回填线程，按 tag id 去重，并把 `/cmd` / `!cmd` 行路由为 Command。出站 `on_send` 经 token bucket 限流后发出 PRIVMSG 帧，并在守护进程提供 `thread_id` 时附加 reply-parent 标签。
