Skip to content

NullpointerW/v2exPusher

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

V2EX Interest Radar

一个基于 V2EX RSS 的个人兴趣订阅后端。持续抓取帖子、维护用户兴趣画像,通过规则或 LLM 语义筛选相关内容,并通过 Telegram Bot 实时推送。

功能概览

  • 全站 RSS 扫描:并发抓取 V2EX ~1200 个节点的 Atom Feed,聚合去重
  • 兴趣画像:用自然语言更新用户偏好关键词、偏好节点、屏蔽词等,持久化至 SQLite
  • 双模式匹配:本地规则匹配(快,无需 API)或 LLM 语义匹配(更准确),支持自动降级
  • 基于画像的节点筛选:LLM 根据用户兴趣从节点目录中筛选 20~80 个相关节点,每个用户只扫自己关心的节点,大幅降低无效抓取
  • Telegram Bot:对话式更新画像、查询命中帖子、自动周期推送、帖子回复监控
  • SSE 实时推流:Dashboard 通过 Server-Sent Events 实时接收概览和 Feed 数据
  • 代理支持:接入 Clash/Mihomo 代理,支持 403 时自动切换代理节点;内置 Shadowsocks 模式
  • Web Dashboard:可视化兴趣画像、命中结果、节点选择列表、推送状态

快速开始

环境要求

  • Rust 1.85+(edition 2024)
  • Windows / macOS / Linux

启动

# 启动 Rust 后端(API、Telegram Bot、自动推送 worker 在同一进程)
powershell -ExecutionPolicy Bypass -File .\start_all.ps1

或直接启动:

powershell -ExecutionPolicy Bypass -File .\start_api.ps1

服务默认监听 http://127.0.0.1:8000,Dashboard 在 http://127.0.0.1:8000/dashboard


配置

项目根目录放 .env 文件,启动时自动加载(环境变量已存在时不覆盖)。

基础

APP_HOST=127.0.0.1
APP_PORT=8000
APP_LOG_LEVEL=INFO

LLM

变量 说明 默认
LLM_PROVIDER openai / qwen openai
LLM_API_KEY API Key
LLM_MODEL 模型名称 gpt-5.4-mini / qwen-turbo
LLM_BASE_URL 兼容 OpenAI 接口的 base URL
LLM_TIMEOUT_SECONDS 请求超时(秒) 30
LLM_MIN_INTERVAL_SECONDS LLM 请求启动之间的最小间隔,避免触发限流 智谱默认 5,其他默认 0
LLM_MAX_CONCURRENCY LLM 网关最大并发请求数,多个慢请求可并发在途 2

Qwen / DashScope 示例:

LLM_PROVIDER=qwen
LLM_API_KEY=replace_with_api_key
LLM_MODEL=qwen-turbo
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_MAX_CONCURRENCY=2

待办

  • 支持配置多个 LLM 供应商、API Key 和模型组合,并提供手动切换、限流后自动切换、失败重试回退等策略,用于降低单一账号 429 限流对聊天理解、节点筛选和帖子匹配的影响。

Telegram Bot

变量 说明
TELEGRAM_BOT_TOKEN BotFather 获取的 Token
TELEGRAM_API_BASE_URL 自定义 API 地址(可选,用于反代)
TELEGRAM_WEBHOOK_URL Webhook 公网地址(不填则使用 polling)
TELEGRAM_WEBHOOK_SECRET Webhook 验证密钥
TELEGRAM_AUTO_PUSH_ENABLED 开启自动推送 true / false
TELEGRAM_AUTO_PUSH_INTERVAL_SECONDS 推送间隔秒数,默认 300
TELEGRAM_AUTO_PUSH_LIMIT 单次最多推送帖子数,默认 5
TELEGRAM_AUTO_PUSH_CANDIDATE_LIMIT 每个节点最多抓取帖子数,默认 30
TELEGRAM_AUTO_PUSH_MATCH_MODE auto / llm / rules,默认 auto
TELEGRAM_WATCH_INTERVAL_SECONDS 帖子回复监控检查间隔,默认 60

代理(Clash / Mihomo)

变量 说明
CLASH_PROXY_ENABLED 开启代理 true / false
CLASH_PROXY_URL HTTP 代理地址,如 http://127.0.0.1:7890
CLASH_CONTROLLER_URL Clash REST API 地址,如 http://127.0.0.1:9090
CLASH_API_SECRET Clash API 密钥
CLASH_SELECTOR_NAME 代理选择器名称,默认 Proxies
CLASH_ROTATE_ON_403 收到 403 时自动轮换代理,默认 true
CLASH_ROTATE_MAX_ATTEMPTS 最大轮换次数,默认 2
CLASH_BUILTIN_SS 使用内置 Shadowsocks 模式
CLASH_SOURCE_PROFILE_PATH Clash 配置文件路径(托管模式)
CLASH_MIHOMO_BINARY_PATH Mihomo 二进制路径(托管模式)

V2EX API

V2EX_API_TOKEN=your_token   # 帖子回复监控备用接口(可选)

API 接口

系统

方法 路径 说明
GET /healthz 健康检查
GET /dashboard Web Dashboard
GET /api/v1/events SSE 实时事件流
GET /api/v1/dashboard/overview 全量概览 JSON
GET /api/v1/llm/status LLM 配置状态

兴趣画像

方法 路径 说明
GET /api/v1/profile 获取画像(?profile_id=xxx
PUT /api/v1/profile 结构化更新画像
DELETE /api/v1/profile 重置画像
POST /api/v1/profile/chat 自然语言更新画像

RSS Feed

方法 路径 说明
GET /api/v1/feeds/v2ex 全站聚合 Feed
GET /api/v1/feeds/v2ex/{node} 指定节点 Feed
GET /api/v1/matches/v2ex 全站命中结果
GET /api/v1/matches/v2ex/{node} 指定节点命中结果

查询参数:limitmatch_modeauto/llm/rules)、matched_onlycandidate_limit

Telegram

方法 路径 说明
GET /api/v1/telegram/status Bot 状态
GET /api/v1/telegram/polling/status Polling 状态
GET /api/v1/telegram/auto-push/status 自动推送状态
POST /api/v1/telegram/polling/run-once 手动触发一次轮询
POST /api/v1/telegram/auto-push/run-once 手动触发一次推送
POST /api/v1/telegram/webhook 接收 Webhook
POST /api/v1/telegram/webhook/set 设置 Webhook
POST /api/v1/telegram/webhook/delete 删除 Webhook
POST /api/v1/telegram/messages/send 主动发消息
POST /api/v1/telegram/push/matches 推送命中内容到指定 chat
GET /api/v1/telegram/topic-watches 查询回复监控列表
POST /api/v1/telegram/topic-watches 添加回复监控
DELETE /api/v1/telegram/topic-watches 删除回复监控
POST /api/v1/telegram/topic-watches/check 手动检查回复监控

核心模块

rust-backend/src/
├── main.rs              # 入口:初始化 tracing、构建 AppState、启动 runtime
├── http_server.rs       # Axum 路由、AppState、SSE、graceful shutdown
├── config.rs            # AppSettings,从 .env / 环境变量读取
├── models.rs            # 所有数据结构(InterestProfile、V2exFeedResponse 等)
├── adapters.rs          # HTTP 网关实现(LLM、Telegram、V2EX topic API)
├── matcher.rs           # 本地规则匹配引擎
├── rss.rs               # V2EX RSS 抓取、节点目录缓存、全局聚合
├── profile/
│   ├── llm.rs           # LlmGateway trait、OpenAiProfileLlmService(含代理重试)
│   ├── store.rs         # 兴趣画像 SQLite 存储
│   ├── node_selector.rs # 节点选择结果存储(profile_node_selections 表)
│   └── chat.rs          # 规则模式画像更新逻辑
├── telegram/
│   ├── bot.rs           # TelegramBotService、API 网关
│   ├── chat.rs          # 对话处理(dispatch → 画像更新 / 监控 / 查询)
│   ├── polling.rs       # Long-polling worker
│   ├── auto_push.rs     # 自动推送、节点选择编排
│   ├── topic_watch.rs   # 帖子回复监控
│   └── store.rs         # chat binding、推送历史 SQLite 存储
└── proxy/
    ├── clash_proxy.rs   # Clash/Mihomo 代理管理、403 轮换、托管启动
    └── ss_local.rs      # 内置 Shadowsocks 支持

基于画像的节点筛选

V2EX 有 ~1200 个节点,全量扫描耗时且无意义。系统会为每个用户维护一份专属节点列表:

选节点流程(启动时预热):

  1. 拉取节点目录(内存缓存 → 磁盘缓存 → 网络,TTL 30 天)
  2. 对每个活跃用户画像,检查 SQLite 中的 profile_node_selections
  3. 若无记录或画像关键词已变动(通过 hash 检测),调用 LLM 分批筛选:
    • 节点目录按每批 150 个切分,串行调用,每批走 run_with_proxy_retry
    • LLM 从每批中选出匹配兴趣的节点,结果用真实目录做 HashSet 过滤
    • 最终合并,持久化至 SQLite

自动推送流程:

启动 → refresh_all_node_selections() → 预热所有画像的节点列表
         ↓
每 N 秒 → run_once()
  └─ 对每个 chat 绑定的画像:
       ├─ 从 SQLite 读取节点列表(hash 匹配则直接用)
       ├─ hash 不匹配 → 重新调 LLM 选节点
       ├─ LLM 失败 → 用旧缓存(stale)
       ├─ 连旧缓存都没有 → 跳过本轮,不回退全局扫描
       └─ fetch_nodes_feed(选定节点) → 匹配 → 推送去重后的新帖子

Telegram Bot 命令

命令 说明
/start 初始化,绑定个人画像
/help 帮助说明
/profile 查看当前兴趣画像
/matches 查看当前命中的帖子
/reset 重置画像
自然语言 更新画像,如"我想关注 AI 工具和独立开发,不看求职"

数据存储

所有数据存储在 data/app.db(SQLite),表结构:

说明
interest_profiles 用户兴趣画像(关键词、节点偏好等)
telegram_chat_bindings Telegram chat 与画像的绑定关系
telegram_pushed_topics 已推送帖子记录(去重用)
telegram_topic_watches 帖子回复监控列表
telegram_kv 自动推送状态、offset 等 KV 状态
profile_node_selections 每个画像对应的节点筛选结果和关键词 hash

节点目录磁盘缓存存储在项目根目录的 node_catalog_cache.json,TTL 30 天。


开发说明

# 编译检查
cargo check

# 运行单元测试
cargo test --lib

# 构建 release
cargo build --release

日志级别通过 APP_LOG_LEVEL 控制,支持 RUST_LOG 覆盖(用于调试特定模块)。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors