惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Blog — PlanetScale
Blog — PlanetScale
PCI Perspectives
PCI Perspectives
K
Kaspersky official blog
T
Tenable Blog
Help Net Security
Help Net Security
Vercel News
Vercel News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
F
Fortinet All Blogs
罗磊的独立博客
P
Palo Alto Networks Blog
爱范儿
爱范儿
Google DeepMind News
Google DeepMind News
T
Threat Research - Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
人人都是产品经理
人人都是产品经理
L
LangChain Blog
Recent Announcements
Recent Announcements
有赞技术团队
有赞技术团队
博客园_首页
D
Darknet – Hacking Tools, Hacker News & Cyber Security
H
Help Net Security
S
Secure Thoughts
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Project Zero
Project Zero
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
V2EX
Last Week in AI
Last Week in AI
H
Heimdal Security Blog
U
Unit 42
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
SecWiki News
SecWiki News
量子位
博客园 - 【当耐特】
Martin Fowler
Martin Fowler
NISL@THU
NISL@THU
S
Securelist
P
Proofpoint News Feed
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
云风的 BLOG
云风的 BLOG
I
Intezer
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园 - Franky
Cisco Talos Blog
Cisco Talos Blog
小众软件
小众软件
C
CXSECURITY Database RSS Feed - CXSecurity.com

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
利用 n8n 打造飞书 RSS 推送机器人 - 少数派
2022-05-17 · via 少数派

前言

本文介绍了基于开源自动化平台 n8n 搭建自动化流程,实现监控 RSS 更新并推送到飞书消息的功能。

文末会列举一些实现此工作流的其他方式,包括发送请求和接收提醒的手段。与此同时,n8n 还可以通过模块组合,实现更多更复杂的功能,本文只作为抛砖引玉。

阅读本文可能需要一定 Linux 基础知识。

n8n 是一个开源的自动化流程搭建工具,可以实现类似 IFTTT 的效果,比如「如果明天下雨,就推送要带伞的消息」。优点是开源、可以自己部署并将信息都储存在本地,同时可以与 Github、Telegram、Slack 等各种服务实现联动,以搭建自动化工作流。

利用 Docker 安裝 n8n

n8n 可以直接下载 Win 或是 Mac 版本,快速在本地使用,但如果想更稳定地长期运行,更适合部署在云服务器、树莓派或 NAS 等工具上。

这里以在云服务器上使用 Docker 进行部署为例,更多安装方式可参考 Installation guides for n8n

假设已经安装好了 Docker,那么 n8n 的部署就非常简单,先新建一个文件夹储存数据。

# 创建数据储存文件夹
mkdir ~/n8n-data

复制运行下面的代码,利用 Docker 安装 n8n。如果云服务器有防火墙,需要把对应的端口打开,这里需要打开云服务器的 TCP 端口 5678

# 利用Docker安装运行n8n
docker run -d \
--name n8n --restart unless-stopped \
-p 5678:5678 \
-v ~/n8n-data:/home/node/.n8n \
-e GENERIC_TIMEZONE="Asia/Shanghai" \
n8nio/n8n 

稍作等待,等 Docker 安装完成后,如果一切顺利,访问 服务器ip地址:5678 就能看到 n8n 的运行页面了,初次进入需要创建账号密码。

n8n 主界面

点击右上角的 New blank workflow 即可开始创建,也可以从软件提供的 Workflow 示例中,选择自己想部署的自动化流程。

这里以搭建一个 RSS 更新自动推送到飞书的机器人为例,展示 n8n 的一些使用方式。

搭建飞书 RSS 推送机器人

以下是我配置好的一个流程模板,复制以下内容粘贴到 n8n 新建 workflow 的页面。

{
  "nodes": [
    {
      "parameters": {
        "url": "https://sspai.com/feed"
      },
      "name": "RSS Feed Read",
      "type": "n8n-nodes-base.rssFeedRead",
      "typeVersion": 1,
      "position": [
        160.5,
        440
      ]
    },
    {
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{new Date($node[\"Latest Read\"].data[\"latestRead\"]).getTime()}}",
              "value2": "={{new Date($node[\"RSS Feed Read\"].data[\"isoDate\"]).getTime()}}"
            }
          ],
          "boolean": [],
          "string": [
            {
              "value1": "={{$json[\"title\"]}}",
              "operation": "contains"
            }
          ]
        }
      },
      "name": "IF",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        560,
        440
      ]
    },
    {
      "parameters": {
        "functionCode": "const staticData = this.getWorkflowStaticData('global');\n\nif (items.length > 0) {\n  staticData.latestRead = items[0].json.isoDate || staticData.latestRead;\n}\n\n\nreturn items;"
      },
      "name": "Write Latest Read",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        760,
        340
      ]
    },
    {
      "parameters": {},
      "name": "NoOp",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        750,
        580
      ]
    },
    {
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyX",
              "value": 1
            }
          ]
        }
      },
      "name": "Cron",
      "type": "n8n-nodes-base.cron",
      "typeVersion": 1,
      "position": [
        -40,
        440
      ]
    },
    {
      "parameters": {
        "requestMethod": "POST",
        "options": {
          "batchInterval": 3000,
          "batchSize": 1
        },
        "bodyParametersUi": {
          "parameter": [
            {
              "name": "msg_type",
              "value": "interactive"
            },
            {
              "name": "card",
              "value": "={\n  \"config\": {\n    \"wide_screen_mode\": true\n  },\n  \"header\": {\n    \"template\": \"black\",\n    \"title\": {\n      \"content\": \"{{$json[\"title\"]}}\",\n      \"tag\": \"plain_text\"\n    }\n  },\n  \"elements\": [\n    {\n      \"tag\": \"div\",\n      \"text\": {\n        \"content\": \"{{$json[\"contentSnippet\"]}}\",\n        \"tag\": \"lark_md\"\n      }\n    },\n    {\n      \"tag\": \"hr\"\n    },\n    {\n      \"elements\": [\n        {\n          \"content\": \"[阅读原文]({{$json[\"link\"]}})\",\n          \"tag\": \"lark_md\"\n        }\n      ],\n      \"tag\": \"note\"\n    }\n  ]\n}"
            }
          ]
        },
        "headerParametersUi": {
          "parameter": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        1000,
        340
      ]
    },
    {
      "parameters": {
        "functionCode": "const staticData = this.getWorkflowStaticData('global');\n\nlatestRead = staticData.latestRead;\n\nfor (let item of items) {\n  item.json.latestRead = latestRead || '2022-05-05';\n  //item.json[\"content:encodedSnippet\"] = item.json[\"content:encodedSnippet\"].replace(/[\\r\\n]/g,\"\\\\n\");\n}\n\nreturn items;"
      },
      "name": "Latest Read",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        360,
        440
      ]
    }
  ],
  "connections": {
    "RSS Feed Read": {
      "main": [
        [
          {
            "node": "Latest Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF": {
      "main": [
        [
          {
            "node": "Write Latest Read",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "NoOp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Latest Read": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cron": {
      "main": [
        [
          {
            "node": "RSS Feed Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Latest Read": {
      "main": [
        [
          {
            "node": "IF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

粘贴后可以看到如下的界面:

粘贴代码得到的 RSS 推送工作流

这里有几处可以配置,第一处是 Cron,设置自动化流程触发的频率,每隔 X 时间间隔运行一次,图中设置为每隔一小时运行。在获取 RSS 时,运行频率不宜过高。如果访问过于频繁,一方面会给对方服务器造成较大负担,同时可能被服务器禁止访问。

Cron 模块设置触发频率

第二个是在 RSS Feed Read 处,填写想订阅的 RSS 地址,这里以少数派 RSS 为例,填写完后点击Excute node,先运行一次获取数据,方便后续设置。

在 URL 处填写想订阅的 RSS 源

第三处(可选)IF 处,设置是否需要针对标题或内容等进行过滤,默认不过滤。

IF 模块进行条件判断

这时先转到飞书,在飞书桌面端,打开一个群(建议先创建一个单人的群进行调试),打开设置,找到群机器人,并点击添加机器人,选择自定义机器人加入群聊,详细操作可以参照 飞书自定义机器人指南

取得飞书自定义机器人 webhook 地址

最后在 HTTP Request 处填入飞书机器人 webhook 地址。

HTTP Request 模块发送 HTTP 请求

填写完成后 Excute node 尝试运行,一切顺利的话就能在飞书中看到推送来的RSS消息了。

机器人在飞书中推送了消息卡片

这里使用了卡片的形式展示消息,若是想调整消息展示样式,可以参考少数派文章 手把手教你用飞书 Webhook 打造一个消息推送 Bot

配置过程一图流

消息机器人安全设置

由于采用 Webhook 的形式,请务必保管好 Webhook 链接,如果泄露可能会导致被推送垃圾信息。为了进一步加道保险,飞书提供了三种安全设置方式,分别是自定义关键词IP 白名单签名校验

前两种方式非常好理解,也都很好设置。自定义关键词是只有当消息中至少含有一个预设的关键词时,才会进行消息推送;IP 白名单则是只推送名单中来源的 IP 所发送的请求。但是这两种方式也有一定的局限性:

  • 关键词有时使消息不够简洁
  • 部署在本地树莓派等设备上时,IP 地址不固定,无法指定
  • 关键词和 IP 白名单各自最多只能添加十个条目

因此这里详细介绍一下在 n8n 中进行签名校验的配置方式。飞书的签名需要将「timestamp + "\n" + 密钥」组合起来当作签名密钥,采用 Hmac SHA256 算法计算签名,再进行 Base64 编码。在发送消息请求时,需要增加对应的 timestampsign  字段。

// 开启签名验证后发送文本消息的请求示例
{
        "timestamp": "1599360473",
        "sign": "xxxxxxxxxxxxxxxxxxxxx",
        "msg_type": "text",
        "content": {
                "text": "The message content is here"
        }
}

在 n8n 中,可以使用 Crypto 模块利用密钥生成签名,复制以下代码粘贴到配置界面,可以得到生成飞书签名用的模块组合。

{
  "nodes": [
    {
      "parameters": {
        "action": "hmac",
        "type": "SHA256",
        "value": "={{''}}",
        "dataPropertyName": "sign",
        "secret": "={{$json[\"timestamp\"]+'\\n'+$json[\"secret\"]}}",
        "encoding": "base64"
      },
      "name": "Crypto",
      "type": "n8n-nodes-base.crypto",
      "typeVersion": 1,
      "position": [
        -80,
        440
      ]
    },
    {
      "parameters": {
        "values": {
          "string": [
            {
              "name": "timestamp",
              "value": "={{Math.round(new Date().getTime()/1000)}}"
            },
            {
              "name": "secret"
            }
          ]
        },
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        -280,
        440
      ]
    }
  ],
  "connections": {
    "Set": {
      "main": [
        [
          {
            "node": "Crypto",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

将上面新增的两个模块按下图方式进行拖拽连接:

拖拽模块两端的点即可将模块进行连接

从飞书机器人设置界面中,勾选签名校验得到密钥,填写在 Set 模块中。

勾选签名校验后记得点保存

接下来将 Latest Read 模块中的代码替换为以下内容,储存计算出的签名,方便在请求的时候调用。

// JS code in the Latest Read Module
const staticData = this.getWorkflowStaticData('global');

latestRead = staticData.latestRead;

for (let item of items) {
  item.json.latestRead = latestRead || '2022-05-05';
  item.json.timestamp = $item("0").$node["Crypto"].json["timestamp"];
  item.json.sign = $item("0").$node["Crypto"].json["sign"];
}

return items;

最后在 HTTP Request 模块中增加校验用的字段:Body Parameters → Add Parameter,添加两个参数,Name 分别为 timestampsign,Value 处点击右侧 Add Expression,分别选择两个对应字段的值。

选择变量的时候可以逐级展开列表,找到目标字段

这样一番倒腾,给飞书机器人模块增加了签名校验,使得信息推送更加安全。当一切配置妥当后,别忘了点击界面右上角的激活,让工作流开始自动运行。

n8n 配置飞书密钥验证一图流

后记

本文介绍了如何用 n8n 打造一个飞书 RSS 推送机器人。订阅什么样的 RSS 来源,可以是网站自身提供的 RSS 地址,也可以利用 RSShub 将各种奇怪的网站转化为 RSS,甚至是利用 kill-the-newsletter 将任意 Newsletter 邮件转化为 RSS 进行追踪。

同时,实现类似工作流的手段还有很多。对于 n8n 这部分,可以使用 IFTTT、Integrately,或是 Github Action 等,实现工作流中「监控 RSS 更新并发送 Webhook 请求」这部分;对于接收提醒,文中利用了飞书作为展示消息的界面,而 n8n 也支持连接到 Telegram、Slack 等通讯软件,或是通过 Send Email 模块实现邮件通知,以及发送到 Cubox、flomo 等各种支持 Webhook 的工具中。

更多功能,更多组合,尽请探索,把闲置的云服务器或是积灰的树莓派等折腾起来吧。

参考资料