爱回收
75.46M · 2026-04-05
这是"一天一个开源项目"系列的第19篇文章。今天带你了解的项目是 Folo(GitHub)。
在信息爆炸的时代,我们每天面对海量的 RSS 源、新闻网站、博客和社交媒体内容。传统的 RSS 阅读器要么功能单一,要么界面复杂,要么缺乏智能化能力。Folo 应运而生,它是一个AI 驱动的下一代信息阅读器,将各种内容源(RSS、列表、集合)统阿里西西小编合到一条时间线上,让你在一个地方就能追踪所有重要信息。更重要的是,它内置了 AI 能力,支持翻译、摘要、智能推荐等功能,让阅读变得更加高效和愉悦。
为什么选择这个项目?
Folo(Follow Everything)是一个开源的 AI 驱动信息阅读器,由 RSSNext 团队开发。它不仅仅是一个 RSS 阅读器,更是一个统一的信息聚合平台,将 RSS 订阅、用户创建的列表、社区集合等多种内容源整合到一条时间线上,让用户能够在一个地方追踪所有重要信息。
项目解决的核心问题:
面向的用户群体:
团队:RSSNext
项目发展历程:
技术栈分布:
Folo 的核心作用是提供一个统一的信息聚合和阅读平台,主要功能包括:
Folo 适用于多种信息阅读和管理场景:
新闻追踪
技术博客聚合
多语言阅读
内容发现
移动阅读
Folo 提供多种安装方式,覆盖所有主流平台:
Web 版本(最简单)
# 直接访问官网即可使用
#
桌面应用
# macOS - App Store
# 搜索 "Folo" 或访问:
#
# Windows - Microsoft Store
# 搜索 "Folo" 或访问:
#
# Linux - 下载 AppImage
# 访问 GitHub Releases 页面下载
#
移动应用
# iOS - App Store
# 搜索 "Folo" 或访问:
#
# Android - Google Play
# 搜索 "Folo" 或访问:
#
社区维护的安装方式
# Arch Linux
yay -S folo-appimage # 由 timochan 和 grtsinry43 维护
# Nix
nix-env -iA nixos.follow # 由 iosmanthus 维护
# macOS Homebrew
brew install --cask folo # 由 realSunyz 维护
# Windows Scoop
scoop install folo # 由 cscnk52 维护
1. 创建账户
首次使用需要创建账户,支持邮箱注册或第三方登录。
2. 添加订阅源
# 方式一:直接添加 RSS 链接
1. 点击"添加订阅源"
2. 输入 RSS/Atom 链接
3. 系统自动识别和验证
# 方式二:搜索发现
1. 使用内置的源发现功能
2. 浏览热门订阅源
3. 一键添加
# 方式三:导入 OPML
1. 从其他阅读器导出 OPML
2. 在 Folo 中导入
3. 批量添加订阅源
3. 开始阅读
# 时间线浏览
- 所有订阅源的内容按时间顺序展示
- 支持标记已读/未读
- 支持收藏重要文章
# AI 功能使用
- 点击文章,使用"摘要"功能快速了解要点
- 使用"翻译"功能阅读外文内容
- 根据阅读习惯获得智能推荐
4. 管理内容
# 分类管理
- 创建文件夹组织订阅源
- 按主题分类管理
- 设置自动标记规则
# 同步设置
- 开启多端同步
- 设置同步频率
- 管理离线下载
统一时间线
AI 增强阅读
多端同步
列表和集合
现代化界面
多媒体支持
无噪音设计
开放生态
与传统 RSS 阅读器和其他信息聚合工具的对比:
| 对比项 | Folo | Feedly | Inoreader | |
|---|---|---|---|---|
| 开源 | 完全开源 | 闭源 | 闭源 | 闭源 |
| AI 功能 | 内置翻译、摘要 | ️ 部分功能需付费 | ️ 部分功能需付费 | 无 |
| 多端同步 | 全平台免费 | ️ 高级功能需付费 | ️ 高级功能需付费 | 免费 |
| 社区功能 | 列表分享、集合探索 | 无 | 无 | 无 |
| 界面设计 | 现代化、简洁 | ️ 功能丰富但复杂 | ️ 功能丰富但复杂 | 简洁 |
| 本地部署 | 可自托管 | 仅云端 | 仅云端 | 仅云端 |
| 价格 | 完全免费 | ️ 基础免费,高级付费 | ️ 基础免费,高级付费 | 免费 |
为什么选择 Folo?
Folo 采用 Monorepo 架构,使用 pnpm workspaces 和 Turbo 进行管理,实现了代码的高度复用和统一维护。
Folo/
├── apps/
│ ├── ssr/ # 服务端渲染应用(Hono + React)
│ ├── desktop/ # Electron 桌面应用
│ └── mobile/ # React Native 移动应用(Expo)
├── packages/
│ └── internal/ # 核心逻辑复用库
│ ├── 状态管理
│ ├── 数据库层(Drizzle ORM)
│ ├── UI 组件库
│ └── 业务逻辑
├── api/ # API 服务层
├── plugins/ # 插件系统
└── scripts/ # 构建和部署脚本
1. 代码复用最大化
packages/internal 包含所有可复用的核心逻辑2. 多端统一体验
3. 技术栈选择
// 前端技术栈
- React 18+:UI 框架
- TypeScript:类型安全
- Tailwind CSS:样式方案
- Drizzle ORM:数据库操作
- Hono:轻量级 Web 框架
// 移动端
- React Native(Expo):跨平台移动开发
- 原生模块:iOS(Swift)、Android(Kotlin)
// 桌面端
- Electron:跨平台桌面应用
- 原生集成:系统通知、快捷键等
功能:
技术实现:
// 订阅源验证
async function validateFeed(url: string) {
try {
const response = await fetch(url);
const xml = await response.text();
const feed = parseRSS(xml);
return {
valid: true,
title: feed.title,
description: feed.description,
items: feed.items
};
} catch (error) {
return { valid: false, error };
}
}
// 订阅源更新
async function updateFeed(feedId: string) {
const feed = await db.feeds.findById(feedId);
const newItems = await fetchFeedItems(feed.url);
const unreadItems = filterUnreadItems(newItems, feed.lastUpdate);
await db.items.bulkInsert(unreadItems);
await db.feeds.updateLastUpdate(feedId);
}
功能:
技术实现:
// 时间线生成
async function generateTimeline(filters: TimelineFilters) {
const feeds = await getSubscribedFeeds(filters.folderId);
const items = await db.items.findByFeeds(feeds, {
unreadOnly: filters.unreadOnly,
dateRange: filters.dateRange,
search: filters.search
});
return items
.sort((a, b) => b.publishedAt - a.publishedAt)
.slice(filters.offset, filters.offset + filters.limit);
}
功能:
技术实现:
// AI 摘要生成
async function generateSummary(articleId: string) {
const article = await db.items.findById(articleId);
const content = extractTextContent(article.content);
// 调用 AI API(如 OpenAI、Claude 等)
const summary = await aiService.summarize({
text: content,
maxLength: 200,
language: 'zh-CN'
});
await db.summaries.save(articleId, summary);
return summary;
}
// 多语言翻译
async function translateContent(
articleId: string,
targetLanguage: string
) {
const article = await db.items.findById(articleId);
const translated = await aiService.translate({
text: article.content,
from: article.language,
to: targetLanguage
});
return translated;
}
功能:
技术实现:
// 同步服务
class SyncService {
async syncToServer(localChanges: LocalChanges) {
// 上传本地变更
await api.sync.upload(localChanges);
// 获取服务器变更
const serverChanges = await api.sync.download({
lastSyncTime: this.lastSyncTime
});
// 合并变更
const merged = this.mergeChanges(localChanges, serverChanges);
// 应用合并结果
await this.applyChanges(merged);
this.lastSyncTime = Date.now();
}
private mergeChanges(local: LocalChanges, server: ServerChanges) {
// 冲突解决策略
// 1. 时间戳优先
// 2. 用户操作优先
// 3. 智能合并
return conflictResolver.merge(local, server);
}
}
功能:
技术实现:
// 列表管理
class ListService {
async createList(userId: string, listData: ListData) {
const list = await db.lists.create({
...listData,
userId,
public: listData.isPublic
});
if (listData.isPublic) {
await this.indexListForDiscovery(list);
}
return list;
}
async shareList(listId: string, shareOptions: ShareOptions) {
const list = await db.lists.findById(listId);
const shareLink = generateShareLink(listId, shareOptions);
if (shareOptions.toCommunity) {
await db.communityCollections.add(list);
}
return shareLink;
}
}
Folo 通过 Monorepo 和共享包实现了最大化的代码复用:
// packages/internal/src/feed-manager.ts
// 所有平台共享的订阅源管理逻辑
export class FeedManager {
// 业务逻辑,不依赖平台
async addFeed(url: string) { /* ... */ }
async updateFeed(feedId: string) { /* ... */ }
}
// apps/ssr/src/routes/feeds.ts
// Web 端使用
import { FeedManager } from '@folo/internal';
// apps/desktop/src/main/feed-handler.ts
// 桌面端使用
import { FeedManager } from '@folo/internal';
// apps/mobile/src/services/feed-service.ts
// 移动端使用
import { FeedManager } from '@folo/internal';
使用统一的状态管理方案(可能是 Zustand、Jotai 或 Redux):
// packages/internal/src/store/feed-store.ts
import { create } from 'zustand';
interface FeedState {
feeds: Feed[];
selectedFeed: Feed | null;
addFeed: (feed: Feed) => void;
selectFeed: (feedId: string) => void;
}
export const useFeedStore = create<FeedState>((set) => ({
feeds: [],
selectedFeed: null,
addFeed: (feed) => set((state) => ({
feeds: [...state.feeds, feed]
})),
selectFeed: (feedId) => set((state) => ({
selectedFeed: state.feeds.find(f => f.id === feedId) || null
}))
}));
使用 Drizzle ORM 进行类型安全的数据库操作:
// packages/internal/src/db/schema.ts
import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core';
export const feeds = pgTable('feeds', {
id: text('id').primaryKey(),
url: text('url').notNull(),
title: text('title').notNull(),
description: text('description'),
lastUpdate: timestamp('last_update'),
folderId: text('folder_id')
});
export const items = pgTable('items', {
id: text('id').primaryKey(),
feedId: text('feed_id').references(() => feeds.id),
title: text('title').notNull(),
content: text('content'),
link: text('link').notNull(),
publishedAt: timestamp('published_at').notNull(),
read: boolean('read').default(false),
starred: boolean('starred').default(false)
});
统一的 AI 服务接口,支持多种 AI 提供商:
// packages/internal/src/ai/ai-service.ts
interface AIService {
summarize(text: string, options: SummaryOptions): Promise<string>;
translate(text: string, options: TranslateOptions): Promise<string>;
recommend(userId: string, context: Context): Promise<Item[]>;
}
class OpenAIAdapter implements AIService {
async summarize(text: string, options: SummaryOptions) {
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{
role: 'system',
content: '你是一个专业的文章摘要生成器...'
}, {
role: 'user',
content: text
}]
});
return response.choices[0].message.content;
}
}
Folo 支持插件扩展(如果已实现):
// 插件接口
interface FoloPlugin {
name: string;
version: string;
init(context: PluginContext): void;
onFeedUpdate?(feed: Feed): void;
onItemRead?(item: Item): void;
}
// 插件示例:自动标签
class AutoTagPlugin implements FoloPlugin {
name = 'auto-tag';
version = '1.0.0';
init(context: PluginContext) {
context.onItemRead((item) => {
const tags = this.extractTags(item);
context.addTags(item.id, tags);
});
}
private extractTags(item: Item): string[] {
// 使用 AI 或规则提取标签
return [];
}
}
如果你想了解更多 RSS 阅读器和信息聚合工具:
Folo 适合以下开发者:
学习价值:
欢迎来我中的个人主页找到更多有用的知识和有趣的产品