暂未播放
0:00
0:00

Fork Friend-Circle-Lite 后我做了什么

3026 字
15 分钟
Fork Friend-Circle-Lite 后我做了什么

为什么又 Fork 了一个友链项目#

我博客一直想要一个友链聚合页——把朋友们最近的 RSS 文章、友链可达性、主页截图汇到一页里。

之前用的 check-flink 撑不起这些了,后面看到原作者 willow-god 又搞了个新项目 Friend-Circle-Lite,于是 fork 下来做了二开。

willow-god
/
Friend-Circle-Lite
🐱一个精简版,无后端,且仅利用github action运行的精简版友链朋友圈程序,兼容fc的json格式信息,同时支持推送友圈更新,支持他人订阅个人站点并在更新时发送邮箱推送。
NOASSERTION
Python

上游已经把核心跑通了,但有几个点跟我自己的用法对不上:友链真源只想放在 FCL 仓库、对外发布的 friends.json 字段名要对齐我的主题、Astro 这类客户端渲染的站反链抓不到、截图和告警没人管。

于是按自己的用法加了一些小功能。

ImYufish
/
Friend-Circle-Lite
🐱一个精简版,无后端,且仅利用github action运行的精简版友链朋友圈程序,兼容fc的json格式信息,同时支持推送友圈更新,支持他人订阅个人站点并在更新时发送邮箱推送。
NOASSERTION
Python

线上效果:fc.yufish.cn,本站的朋友圈、友链数据和截图延迟都来自这个项目。


一、相对上游,我加了些什么#

  • 新增 spider_settings.sourcelocal / 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 转发消息
ImYufish
/
blog-bot
Description not set
no-license
JavaScript

顺手补了 CNB 流水线#

加了一份 .cnb.yml(cnb.cool 的流水线),和 GitHub Actions 二选一,跑的是同一组命令。


二、整套数据流长什么样#

仅人工手推

pull/push

needs

tree比对变了才force

普通push

该验

pass

不pass

访客回复评论

写回真源

dispatch

友链方回复

friends.json · main 分支

唯一真源

cache 分支

cache.sqlite3 orphan 单文件

Friend Circle Lite 工作流

schedule 每4h · dispatch · push

Job1 检测

pull cache→抓RSS/可达性/反链

→postprocess: 告警diff

→Issue巡检回评→push cache

→强推 page

Job2 截图 needs Job1

Selenium 增量截友链主页

普通 push page

Job3 保活 always

page 分支产物

index/link/all/friends.json

Vercel · fc.yufish.cn

博客 friends.astro

useRemote 渲染

Issue opened/reopened

或 comment ·「友链申请」

解析·去重·闸门「待更新」

Playwright 核验

可达性+反链含 AUTHOR_URL

pass→写回 friends.json

关 Issue

不pass→打「待更新」开放

gh workflow run 主流程

仅 shouldVerify

alert 检出状态翻转

不可达/反链丢失/恢复

持续离线档位/反链缺失

POST /api/alert

blog-bot Worker

QQ 主动消息→我

失败兜底企微

巡检异常→评论Issue

打待更新;恢复移除

仅人工手推

pull/push

needs

tree比对变了才force

普通push

该验

pass

不pass

访客回复评论

写回真源

dispatch

友链方回复

friends.json · main 分支

唯一真源

cache 分支

cache.sqlite3 orphan 单文件

Friend Circle Lite 工作流

schedule 每4h · dispatch · push

Job1 检测

pull cache→抓RSS/可达性/反链

→postprocess: 告警diff

→Issue巡检回评→push cache

→强推 page

Job2 截图 needs Job1

Selenium 增量截友链主页

普通 push page

Job3 保活 always

page 分支产物

index/link/all/friends.json

Vercel · fc.yufish.cn

博客 friends.astro

useRemote 渲染

Issue opened/reopened

或 comment ·「友链申请」

解析·去重·闸门「待更新」

Playwright 核验

可达性+反链含 AUTHOR_URL

pass→写回 friends.json

关 Issue

不pass→打「待更新」开放

gh workflow run 主流程

仅 shouldVerify

alert 检出状态翻转

不可达/反链丢失/恢复

持续离线档位/反链缺失

POST /api/alert

blog-bot Worker

QQ 主动消息→我

失败兜底企微

巡检异常→评论Issue

打待更新;恢复移除


三、博客侧怎么接(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/alertPOSTFCL 后处理告警推送,QQ 单聊主动消息

五、部署:变量到底怎么配#

这部分是踩坑后最该写清楚的地方——上游文档对 Secrets 语焉不详,我按真实工作流把变量列全。

5.1 FCL 仓库:GitHub Actions + Vercel#

流水线 .github/workflows/friend_circle_lite.yml 三个触发入口:每 4 小时定时(00 */4 * * *)、推 friends.jsonmain 自动巡检、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.jsonno-cache,图片字体等长缓存。

需要在 FCL 仓库 Settings → Secrets and variables → Actions 里配的 Secrets:

Secret用途必填
GITHUB_TOKENpage 分支、触发 workflow、清理运行记录内置自动提供,不用手动建
QQ_BOT_ALERT_URLblog-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 注入(不进仓库):

Terminal window
wrangler secret put WX_TOKEN # 企微回调 Token(只用企微才需要)
wrangler secret put WX_AESKEY # 企微 EncodingAESKey
wrangler secret put WX_CORPID # 企业 ID
wrangler secret put WX_SECRET # 应用 Secret
wrangler 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_PATHQQ 机器人 AppID 与回调路径
SITE_URL / R2_PUBLIC_URL站点域名与图床公开域名
DEPLOY_MODEcf = 纯 Cloudflare 自解密;hybrid = 作 EdgeOne 的 /internal 后端

KV(草稿 / access_token 缓存)和 R2(图床)在 wrangler.toml 以 binding 形式声明,不是 Secret。


六、怎么触发#

自动触发#

时间内容
每 4 小时(00 */4 * * *抓取 + 检测 + 后处理全套
friends.jsonmain真源变更后立即重跑一轮
[友链申请] 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——这些都不是多高深的东西,但拼起来正好适合我的使用场景。


相关链接#

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
Fork Friend-Circle-Lite 后我做了什么
https://x1anyu.cn/posts/14/
作者
羡鱼
发布于
2026-09-02
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
羡鱼
临渊空慕水中鱼, 不如携风自渡河.
分类
标签
最新动态
站点统计
文章
14
分类
7
标签
35
总字数
19,969
运行时长
0
最后活动
0 天前
文章目录