内容
# 卡片新闻
> 🇰🇷 한국어 문서는 [README.ko.md](./README.ko.md) 를 보세요.
一款 **Claude Code 插件**,用于生成适合发布到Instagram和Threads的**Instagram风格卡片新闻**(1080×1350 PNG)—— 从选择主题到发布到Instagram和Threads。该插件提供整个自动化流程作为**可组合的技能**,您可以将其作为整体运行或逐一运行。
为 **SURFERS** (AI·AX 离线教育)而建—— 默认流程选择一个AI/LLM主题,撰写适合初学者阅读的中文副本,以SURFERS品牌主题渲染,归档到团队Wiki,并自动发布。您可以更换主题和来源,以针对您的品牌。
## 流程
| # | 技能 | 功能 | 门控 |
|---|-------|------|------|
| 1 | *(主题选择)* | 爬取 `news.hada.io/rss/news`,选择最受欢迎的品牌相关主题 | — |
| 2 | `cardnews-research` | 拉取文章 + 链接来源 → 核心信息、关键点、钩子、空白 | — |
| 3 | `cardnews-copy` | 编写渲染规格 (`spec.json`) | **模式 + 字符数校验** |
| 4 | `cardnews-image` | 填充每个卡片的图像:**①** 主题自己的文章/链接,**②** Google/Unsplash 爬取 | 爬取失败 → 请求用户 |
| 5 | `cardnews-render` | 在本地渲染 1080×1350 PNG(使用 Playwright),**surfers** 主题 | 渲染器内置 + Chromium |
| 6 | `cardnews-wiki` | 将笔记 + PNGs 存档到 `llm-wiki` git 仓库并推送 | 仓库可达 |
| 7 | `cardnews-autopublish` | 通过本地发布运行时发布 Instagram + Threads 轮播图 | **审批清单** |
| — | `cardnews-workflow` | 协调步骤 1–7 端到端 | — |
**可选 / 独立**
- `cardnews-review` — Slack 签核 → 编写 `cardnews-autopublish` 需要的审批清单。不在默认流程中。
- `cardnews-notion` — 旧版 Notion 保存(被 `cardnews-wiki` 取代)。
使用 **`cardnews-workflow`** 运行整个流程,或调用任何单个技能以构建自己的流程:仅渲染、复制 + 渲染、存档而不发布等。
## 安装
```bash
claude plugin marketplace add https://github.com/AX-Surfers/card-news-mcp
claude plugin install card-news@cardnews
```
(对于本地开发,`claude plugin marketplace add /path/to/this/repo`。)
### 渲染器设置(一次)
渲染步骤使用捆绑的 Playwright 渲染器。构建它并安装 Chromium:
```bash
cd <plugin-root> && npm install && npm run build && npx playwright install chromium
```
## 渲染器 (`render-cli`)
渲染步骤调用一个小型 CLI,将规格 JSON 转换为 PNG:
```bash
node dist/render-cli.js <spec.json> [--out <dir>]
```
输入规格形状:
```jsonc
{
"id": "my-post", // 可选,用于输出文件夹名称
"theme": "surfers", // 可选,主题名称或路径
"cards": [
{ "type": "thumbnail", "index": 0, "title": "…", "category": "NEWS", "image_url": "https://…" },
{ "type": "body", "index": 1, "page_number": 1, "title": "…", "body": "…", "image_url": "https://…" },
{ "type": "closing", "index": 2, "tagline": "…", "cta": "…", "image_url": "https://…" }
],
"brand": { // 可选,按调用覆盖主题默认值
"brand_name": "SURFERS",
"tagline": "Follow for more",
"cta": "Follow us <b>now</b>",
"primary_color": "#0573F0"
}
}
```
输出(stdout JSON):
```jsonc
{
"id": "my-post",
"theme": "surfers",
"rendered_cards": [
{ "index": 0, "type": "thumbnail", "path": "/abs/path/0_thumbnail.png", "base64": "data:image/png;base64,…" }
]
}
```
### 卡片类型
| 类型 | 内容 |
|------|--------------|
| `thumbnail` | 全宽背景图像,品牌标志,类别徽章,大标题 |
| `body` | 背景图像,页码徽章,标题,正文,品牌标志 |
| `closing` | 品牌标志,标语,行动号召,保存/点赞/关注图标 |
### 复制校验(模式门控)
`cardnews-copy` 仅在每个卡片通过后发出规格(默认主题;可按主题调整):
| 规则 | 限制 |
|------|-------|
| 恰好 1 个 `thumbnail`(第一个)+ 1 个 `closing`(最后一个) | 必需 |
| `body` 卡片 | 1..N |
| `thumbnail` 类别 / 标题 | ≤ 10 / ≤ 40 字符 |
| `body` 标题 / 正文 | ≤ 30 / ≤ 200 字符 |
| `closing` 标语 / 行动号召 | ≤ 24 / ≤ 80 字符 |
| 任何标题/正文 | 非空 |
## 主题
内置两个主题:
- **`surfers`** — SURFERS 品牌主题(蓝色 `#0573F0`,Pretendard)。默认流程使用。
- **`default`** — 清洁,单色(灰阶)。中性起点。
使用 `spec.theme`、`THEME` 环境变量或 `THEME_DIR` 路径选择一个主题。
想要自己的颜色,字体和标志?参见 **[DESIGN.md](./DESIGN.md)** — 自定义主题是一个 `theme.json` + 三个 HTML 文件 + 一些 SVGs,无需代码。
```bash
THEME_DIR=/path/to/my-theme
```
## 存储后端
渲染输出位置,通过 `STORAGE_BACKEND` 选择:
| 值 | 行为 | 额外设置 |
|-------|----------|-------------|
| `local`(默认) | 将 PNG 保存到 `OUTPUT_DIR`,也返回 base64 | 无 |
| `base64` | 仅返回 base64 数据 URI(无文件) | 无 |
| `supabase` | 上传到 Supabase Storage,返回公共 URL | `npm i @supabase/supabase-js`,设置 `SUPABASE_URL`,`SUPABASE_SERVICE_ROLE_KEY` |
| `s3` | 上传到 S3 / Cloudflare R2,返回公共 URL | `npm i @aws-sdk/client-s3`,设置 `S3_*` 变量 |
> **发布不需要公共渲染后端。** `cardnews-autopublish` 在发布时将渲染的 PNG 上传到公共 Google Drive URL,因此普通的本地渲染输出就足够了(Instagram/Threads 只从公共 URL 获取图像)。
## 渲染器环境变量
| 变量 | 默认值 | 目的 |
|----------|---------|---------|
| `THEME` | `default` | 内置主题名称(流程使用 `surfers`) |
| `THEME_DIR` | — | 外部自定义主题路径 |
| `STORAGE_BACKEND` | `local` | `base64` / `local` / `supabase` / `s3` |
| `OUTPUT_DIR` | `./card-news-out` | `local` 保存文件的位置 |
## 发布运行时 (`cardnews-autopublish`)
发布运行本地脚本在自包含的运行时中 — **无 hermes,无 docker,无 Notion**:
```
~/.local/share/cardnews/ ($CARDNEWS_DATA_ROOT 可重新定位)
├── .env / .secrets/ 令牌(600/700,gitignored,永不打印)
├── scripts/ 修补发布脚本
├── work/ 临时文件(PNG,URL 列表,结果)
└── run_publish.sh 加载 .env → 运行验证 / 上传公共 / 发布
```
- **凭证**(仅名称;值保留在 `.env`/`.secrets` 中,从不回声):`INSTAGRAM_GRAPH_TOKEN`(或 `META_GRAPH_ACCESS_TOKEN`)+ `INSTAGRAM_BUSINESS_ACCOUNT_ID`,`THREADS_ACCESS_TOKEN` + `THREADS_USER_ID`,`GOOGLE_DRIVE_SERVICE_ACCOUNT_FILE`,`GOOGLE_DRIVE_PARENT_ID`(必须是 **共享驱动** — 服务帐户有 0 个个人驱动配额)。
- **审批门控。** 发布阻止除非 `approval.json` 清单断言 `{ "approved": true, "approval_source": "...", "approved_at": "..." }`。永不伪造审批 — 获取明确的批准,然后编写清单(或使用 `cardnews-review` 的 Slack 签核生成它)。
- **一个平台或两个。** `--platform {both,instagram,threads}`(默认 `both`)。
## Wiki 存档 (`cardnews-wiki`)
每个作品都存档到团队的 `llm-wiki` git 仓库(`~/Develop/llm-wiki`,远程 `AX-Surfers/marketing-llm-wiki`,分支 `main`)下的 `01-콘텐츠마케팅/카드뉴스/<id>/` 作为 Obsidian 笔记(`## 관련` MOC 链接)+ PNG,然后提交并推送。这是生成的永久存储库;它取代了旧的 Notion 保存。
## 故障排除
- **Chromium 下载很大 / 被阻止。** 在安装期间设置 `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` 并将 Playwright 指向现有的 Chrome/Chromium。
- **韩文 / CJK 文本显示不正确。** 主题加载 Pretendard 网络字体;确保渲染机器人在首次渲染时有互联网连接,或在主题中捆绑本地字体。
- **Google Drive 上传 403。** `GOOGLE_DRIVE_PARENT_ID` 必须是服务帐户所属的共享驱动,而不是我的驱动。
## 开发
```bash
npm install
npm run typecheck
npm run test:render surfers # 渲染样本卡片到 ./card-news-out
npm run build
node dist/render-cli.js spec.json --out ./card-news-out
```
## 许可证
[MIT](./LICENSE)