Fork Friend-Circle-Lite 后我做了什么
为什么又 Fork 了一个友链项目
我博客一直想要一个友链聚合页——把朋友们最近的 RSS 文章、友链可达性、主页截图汇到一页里。
之前用的 check-flink 撑不起这些了,后面看到原作者 willow-god 又搞了个新项目 Friend-Circle-Lite,于是 fork 下来做了二开。
上游已经把核心跑通了,但有几个点跟我自己的用法对不上:友链真源只想放在 FCL 仓库、对外发布的 friends.json 字段名要对齐我的主题、Astro 这类客户端渲染的站反链抓不到、截图和告警没人管。
于是按自己的用法加了一些小功能。
线上效果:fc.yufish.cn,本站的朋友圈、友链数据和截图延迟都来自这个项目。
一、相对上游,我加了些什么
- 新增
spider_settings.source:local/remote切换开关 friends_input.rename读写共用一份桥接- 加 Playwright 无头兜底,访问 JS 渲染站
eo_ping_url国内延迟探针 +verified人工核验- 主页截图
postprocess.siteshot+refresh_days - 地域屏蔽诊断
postprocess.geo_diagnose - 增加 Issue 自助申请友链 + 巡检回评
- QQ 机器人 + 企业微信双通道告警
- 新增CNB平台(
.cnb.yml)
真源 local/remote 开关 + 字段桥接
原版把友链真源远程读取 json_url。我加了个 spider_settings.source 开关:
local:真源在 FCL 仓库的friends.json,Issue 申请通过后自动写回这里;remote:真源在外部博客的/friends.json端点,FCL 只巡检展示、不维护,对应 Issue 自助申请整套关闭。
同一份字段映射既管读取又管发布:我博客用 title/siteurl/imgurl,FCL 内部用 name/link/avatar,两边都不用迁就对方。配起来就是 conf.yaml 里这几行:
spider_settings: source: local list_key: "friends" friends_input: rename: name: "title" link: "siteurl" avatar: "imgurl" linkpage: "" verified: ""值留空 "" 表示直接用 FCL 原生字段名,不做映射;填了就按外部端点字段名取/重命名。这样不同的端点字段名不一也能零改动适配。
反链检测:能认 JS 渲染的站了
原版只在静态 HTML 里找你的域名。可部分网站的友链是客户端渲染的,静态 HTML 里根本没有友链列表,反链永远判「无」。我加了 Playwright 无头渲染兜底(backlink_headless),没装 playwright 时自动回退静态匹配、不阻断主流程,装了就用无头浏览器渲染后再判定。
可达性:国内探针 + verified 人工核验
由于GitHub Actions服务器在美国,访问国内网站延迟会失真,于是我加了 eo_ping_url 国内延迟探针,额外测一个国内视角并自动取优显示;另加了 verified 人工核验标记(默认关),用于给个别网站添加白名单,避免反链误报反复打扰。
link_check: enable_backlink_check: true author_url: "x1anyu.cn" eo_ping_url: "" # 留空则走仓库环境变量 EO_PING_URL backlink_headless: true # 未装 playwright 自动跳过主页截图会过期重拍
postprocess.siteshot.refresh_days 控制截图有效期,图龄超过天数自动重新截:
postprocess: siteshot: enable: true upload_folder: "friends" max_workers: 2 refresh_days: 7 # 7 天前的截图自动重拍;0 = 永久有效自助申请友链 + 巡检回评
apply-friend.yml 支持 Issue 形式的友链自助申请:访客提交 [友链申请] Issue → 工作流解析 → Playwright 验证可访问性 + 反链 → 通过即写回 friends.json,失败打 待更新 标签。
主流程里再用 github-script 做巡检回评:不可达或反链丢失就自动给对应申请 Issue 回评、打标签,恢复了再撤。verified 站点不会因反链误报被反复打扰。
后处理
postprocess 拆成三段,开关集中在 conf.yaml,CI 只注入敏感信息与路径:
geo_diagnose:不可达站点地域屏蔽二次诊断(含国内视角二次探测);siteshot:截图回填 + 图床上传;alert:状态翻转告警,QQ 机器人经 blog-bot 转发消息
顺手补了 CNB 流水线
加了一份 .cnb.yml(cnb.cool 的流水线),和 GitHub Actions 二选一,跑的是同一组命令。
二、整套数据流长什么样
三、博客侧怎么接(my-blog / Firefly 主题)
我博客是 Firefly 主题。友链页面修改数据在线获取自 FCL,本地 friendsConfig.ts 作为兜底数据,同时写一份同步脚本每日同步,每天12点自动检测远程,有改动就自动添加,无改动则静默。
src/config/friendsConfig.ts 里只开一个开关:
export const friendsPageConfig: FriendsPageConfig = { useRemote: true, // true = 拉 fc.yufish.cn 实时数据;false = 仅用本地兜底 remoteBaseUrl: "https://fc.yufish.cn", // 页面从此取 friends.json / link.json imgProxy: { // 封面图同域反代,走博客 CDN 避免访客直连图床 enabled: true, fromHost: "imgbed.yufish.cn", toHost: "x1anyu.cn", },};src/pages/friends.astro 的拉取逻辑分两步、各自有回退:
- 拉
link.json拿封面截图和延迟状态; - 拉
friends.json拿友链清单,远程优先、失败回退到本地friendsConfig.ts; - 客户端还做了
fetch(..., { cache: "no-store" })的实时刷新,页面打开即重渲染。
四、机器人怎么接(blog-bot / Cloudflare Worker)
博客机器人是一套跑在 Cloudflare Workers 上的服务。它给 FCL 提供了告警端点:
| 端点 | 方法 | 作用 |
|---|---|---|
/api/alert | POST | FCL 后处理告警推送,QQ 单聊主动消息 |
五、部署:变量到底怎么配
这部分是踩坑后最该写清楚的地方——上游文档对 Secrets 语焉不详,我按真实工作流把变量列全。
5.1 FCL 仓库:GitHub Actions + Vercel
流水线 .github/workflows/friend_circle_lite.yml 三个触发入口:每 4 小时定时(00 */4 * * *)、推 friends.json 到 main 自动巡检、workflow_dispatch 手动。步骤是:恢复 cache 分支缓存 → run.py 抓取检测 → postprocess all 后处理 → Issue 回评 → 组装静态产物强推 page 分支。并发做了串行化,避免定时 / 手动 / 截图 job 互相覆盖。
Vercel 这边 vercel.json 只一句,只从 page 分支部署:
{ "git": { "branch": "page", "deploymentEnabled": { "main": false } }}缓存由根目录 _headers 控制(注意:Vercel 只读根 _headers,不读 static/edgeone.json,后者是死配置),给 index.html / all.json / link.json / errors.json / friends.json 设 no-cache,图片字体等长缓存。
需要在 FCL 仓库 Settings → Secrets and variables → Actions 里配的 Secrets:
| Secret | 用途 | 必填 |
|---|---|---|
GITHUB_TOKEN | 推 page 分支、触发 workflow、清理运行记录 | 内置自动提供,不用手动建 |
QQ_BOT_ALERT_URL | blog-bot 的 /api/alert 地址 | 想收告警则填 |
QQ_BOT_ALERT_TOKEN | 与 blog-bot 的 ALERT_TOKEN 一致 | 同上 |
WECOM_WEBHOOK_URL | 企业微信 webhook 兜底渠道 | 可选 |
IMG_UPLOAD_URL | 图床上传端点(siteshot 用) | 想用截图则填 |
IMG_AUTH_CODE | 图床鉴权码 | 同上 |
PROXY_URL | 代理地址(国内抓 RSS / 检测) | 可选 |
EO_PING_URL | 国内延迟探针端点(对应 eo_ping_url) | 可选 |
AUTHOR_URL | 反链检测的本站域名(apply-friend.yml 用,默认 x1anyu.cn) | 可选 |
SMTP_PWD | 邮件功能密码 | 基本不用(email_push / rss_subscribe 均关) |
conf.yaml 里凡标注「推荐走环境变量、配置留空」的项,就对应上面这些 Secrets;填了 Secrets 后 conf.yaml 里保持空串即可。
5.2 blog-bot:Cloudflare Worker
非敏感变量写在 wrangler.toml 的 [vars] 里(随代码提交),敏感信息用 wrangler secret put 注入(不进仓库):
wrangler secret put WX_TOKEN # 企微回调 Token(只用企微才需要)wrangler secret put WX_AESKEY # 企微 EncodingAESKeywrangler secret put WX_CORPID # 企业 IDwrangler secret put WX_SECRET # 应用 Secretwrangler secret put ALERT_TOKEN # /api/alert 鉴权 token,须与 FCL 的 QQ_BOT_ALERT_TOKEN 一致wrangler secret put GH_TOKEN # GitHub PAT(repo 权限),写文章/动态/开友链 Issue 用wrangler secret put QQ_PRIVATE_KEY # QQ 机器人 Ed25519 私钥(只用 QQ 才需要)wrangler.toml 里已固化的关键 [vars]:
| 变量 | 含义 |
|---|---|
GH_OWNER / GH_REPO / GH_BRANCH / GH_PATH | 博客仓库定位(写文章 / 动态) |
FL_OWNER / FL_REPO / FL_BRANCH / FL_PATH | 友链真源仓库定位(FCL 的 friends.json) |
QQ_APPID / QQ_CALLBACK_PATH | QQ 机器人 AppID 与回调路径 |
SITE_URL / R2_PUBLIC_URL | 站点域名与图床公开域名 |
DEPLOY_MODE | cf = 纯 Cloudflare 自解密;hybrid = 作 EdgeOne 的 /internal 后端 |
KV(草稿 / access_token 缓存)和 R2(图床)在 wrangler.toml 以 binding 形式声明,不是 Secret。
六、怎么触发
自动触发
| 时间 | 内容 |
|---|---|
每 4 小时(00 */4 * * *) | 抓取 + 检测 + 后处理全套 |
推 friends.json 到 main | 真源变更后立即重跑一轮 |
开 [友链申请] Issue | 申请通过后自动写回并触发主检测 |
手动触发(按需)
进仓库 Actions → Friend Circle Lite → Run workflow,只有一个参数:
target_link:填友链名称或 URL 关键词(多个用|或逗号分隔),只处理匹配的那几个;留空 = 按增量规则处理全部。改这个就能「只截某几个友链的图 / 只重测某几个」,不会把其他友链的数据冲掉。
自助申请
在我博客友链页填表单 → 开 [友链申请] Issue → apply-friend.yml 解析并 Playwright 验证 → 通过写回 friends.json。失败会打 待更新 标签,修复后在 Issue 下回任意内容即可重验。
七、常见场景
| 我想… | 怎么做 |
|---|---|
| 日常看看谁挂了 | 等每 4h 自动跑,或 Actions → Run workflow(target_link 留空) |
| 新加了个友链,只测它 | Run workflow,target_link 填名字或 URL 关键词 |
| 给别人加友链 | 在 friends.json 添加友链 → 自动触发验证 |
| 某友链失效被标「待更新」 | 修复后在对应 Issue 下回任意内容 → 自动重验 → 通过撤标签 |
| 截图太旧了 | Run workflow 填 target_link 触发增量截图,或等 refresh_days 自动重拍 |
八、几个设计取舍
截图会过期,且有兜底链。 refresh_days 控制图龄,超期自动重拍;截图上传走图床,图床挂了也有在线兜底服务,不会白屏。Selenium 那套在 CI 装 Chrome 要两三分钟,所以截图拆成独立 job,不和每 4h 的轻量检测抢时间。
反链用 verified 挡误报。 客户端渲染的站靠 Playwright 兜底能认,但仍有个别反爬站抓不到。与其天天被误报吵,不如手动标 verified 让巡检跳过它的反链检查——这是给站长自己留的「我确认过」开关。
状态翻转才告警,不是每轮都推。 notify.run 先比对本轮和上轮 link.json,没变化就静默;有变化且 alert.enable 开着才推。QQ 通道失败会自动降级到企业微信,两条都没配就跳过,绝不阻断主流程。
真源只维护一份。 友链清单只在 FCL 仓库的 friends.json 维护(local 模式),博客自动拉取、连本地兜底都是脚本同步出来的。加友链只需要在这边添加,不用两边改。
收尾
fork 一个别人维护得挺好的项目,最舒服的地方就是能在它结实的地基上,按自己的用法挑几处改。截图、地域诊断、Playwright 反链兜底、Issue 自助申请、QQ 告警、CNB——这些都不是多高深的东西,但拼起来正好适合我的使用场景。
相关链接
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!











