Skip to main content

FenLiu (分流)

Created by marvin8 with assistance from Claude and DeepSeek AI assistants.

⚠️ DISCLAIMER / PROVISO: This project is a work in progress with major changes still happening. It is in no way anywhere close to finished and is only borderline useful for actual production use. Expect breaking changes, incomplete features, and significant architectural evolution as development continues.

Divide the Fediverse content flow

FenLiu is a web application that monitors Fediverse hashtags, filters spam, allows human review, learns from feedback, and exports quality content for boosting. Inspired by the ancient Chinese Dujiangyan irrigation system (256 BC) that separated silt from water, FenLiu applies 2,300-year engineering wisdom to modern digital content streams.

Current Status — v0.7.1

Fully functional spam filtering and content management system with complete Curated Queue integration, flexible pattern-based user blocking, automated queue lifecycle management, production-ready containerization, and ML training data collection.

Latest (v0.7.1): Random post selection for the Curated Queue API. 620 tests passing.

Documentation

📚 Live Docs: https://fenliu.marvin8.zone

The docs/ folder contains full MkDocs documentation covering installation, API reference, pattern blocking, Curated Queue integration, and more.

mkdocs serve   # serve locally with hot reload
mkdocs build   # build static site

Features

Core Functionality

  • Hashtag Monitoring: Monitor multiple Fediverse hashtags with customizable instance sources and scheduling
  • AI Classification: AI-powered text-promotion and image real-cat checks with review badges and filters (no auto-reject by default)
  • Manual Review Interface: Approve/reject posts with AI classification badges; pagination, bulk actions, and back-to-top link
  • Curated Queue Export: Reliable API-driven queue with ack/nack/error pattern

Reblog Controls (Export Filters)

  • Pattern-Based User Blocking: exact, suffix, prefix, and contains matching modes
  • Hashtag Blocklist: Exclude posts containing blocked hashtags
  • Attachments-Only Mode: Export only posts with media attachments
  • Auto-Reject on Fetch: Automatically reject blocked content before review
  • Blocklist Refresh: Apply Settings changes to the review page instantly

Web Interface

  • Dashboard, Streams Management, Review Workflow, Pattern Blocking Settings, Queue Preview, Statistics
  • Responsive design — no external JavaScript dependencies

REST API

  • Hashtag streams, posts, curated queue, reblog controls, statistics, health
  • API key authentication for queue endpoints

Technical Quality

  • 620 tests, 100% passing
  • Comprehensive type hints; zero type errors under ty check
  • All functions pass ruff and complexipy checks
  • Alembic migrations run automatically on startup

Quick Start

Prerequisites: Python 3.12+, uv package manager

uv sync -U --all-groups
fenliu --reload --debug

Open http://localhost:8000, create a hashtag stream, fetch posts, and review them.

Container Deployment

podman build -t fenliu -f Containerfile .
cp .env.example .env   # edit with your settings
podman run -d -p 8000:8000 --env-file .env \
  -v fenliu-data:/app/data -v fenliu-logs:/app/logs fenliu

See the Container Deployment guide for full details.

API Endpoints

All curated queue endpoints require X-API-Key header (generate in Settings).

Streams & Posts:

  • GET /api/v1/streams — List streams
  • POST /api/v1/streams — Create stream
  • GET/PUT/DELETE /api/v1/streams/{id} — Stream operations
  • POST /api/v1/streams/{id}/fetch — Fetch posts for stream
  • POST /api/v1/streams/fetch-all — Fetch all active streams
  • GET /api/v1/posts — List posts with filtering
  • PATCH /api/v1/posts/{id} — Update post (review, approve)
  • GET /api/v1/stats — Application statistics

Curated Queue:

  • GET /api/v1/curated/next — Next post (204 if empty); ?random=true for random selection
  • POST /api/v1/curated/{post_id}/ack — Confirm successful reblog
  • POST /api/v1/curated/{post_id}/nack — Return to queue (transient failure)
  • POST /api/v1/curated/{post_id}/error — Mark permanently failed
  • POST /api/v1/curated/{post_id}/requeue — Return errored post to queue
  • POST /api/v1/curated/cleanup — Delete old delivered posts
  • POST /api/v1/curated/trim-pending — Trim excess pending posts

Reblog Controls:

  • GET/PUT /api/v1/reblog-controls/settings — Reblog filter settings
  • GET/POST /api/v1/reblog-controls/blocked-users — Blocked users (pattern-based)
  • DELETE /api/v1/reblog-controls/blocked-users/{id} — Remove blocked user
  • GET/POST /api/v1/reblog-controls/blocked-hashtags — Blocked hashtags
  • DELETE /api/v1/reblog-controls/blocked-hashtags/{id} — Remove blocked hashtag
  • POST /api/v1/reblog-controls/reject-blocked — Bulk reject matching posts

System: GET /health, GET /info

See the API docs for full reference.

Configuration

Key environment variables (see .env.example for full list):

Variable Default Description
DATABASE_URL sqlite:///./fenliu.db Database connection
SECRET_KEY (none — required) Session key; app refuses to start with placeholder
UI_AUTH_ENABLED true Web UI login (single user: admin). Set false only for personal/private-LAN deployments
DEFAULT_INSTANCE mastodon.social Default Fediverse instance
DEBUG false Enable debug logging
AI_API_BASE_URL https://api.moonshot.ai/v1 OpenAI-compatible AI API base URL
AI_API_KEY (none — required) AI API key
AI_TEXT_MODEL kimi-k2.6 Model for the text-promotion check
AI_VISION_MODEL kimi-k2.6 Model for the image real-cat check
AI_BATCH_HOUR 2 Local hour the nightly image batch runs

Development

uv run tryke test                # run tests (from repo root: uv run tryke test --root packages/fenliu)
uv run ruff check .              # lint
uv run complexipy .              # complexity check
uv run ty check                  # type check
nox                              # full CI simulation

alembic upgrade head             # apply migrations
alembic revision --autogenerate -m "description"  # new migration

Project Structure

fenliu/
├── src/fenliu/
│   ├── main.py                  # PyView application and LiveViews
│   ├── models.py                # SQLAlchemy models
│   ├── schemas.py               # Pydantic validation
│   ├── api/                     # REST API (curated, reblog_controls, api_keys)
│   ├── services/                # Business logic (spam scoring, fediverse, scheduler)
│   ├── templates/               # HTML templates
│   └── static/                  # CSS and assets
├── alembic/                     # Database migrations
├── tests/                       # Test suite (620 tests)
└── docs/                        # MkDocs documentation

Technical Stack

  • Framework: PyView (Starlette-based LiveView)
  • Database: SQLAlchemy + SQLite, Alembic migrations
  • Validation: Pydantic v2
  • Fediverse: minimal-activitypub
  • Frontend: Jinja2 + Tailwind CSS, no external JS
  • Testing: Tryke (620 tests)
  • Tooling: ruff, ty, complexipy, uv

What's New in v0.7.1

  • Random queue selection: GET /api/v1/curated/next?random=true returns a randomly chosen eligible post instead of the oldest. If the chosen author has more than one pending post, their oldest is returned to avoid consecutive same-author posts
  • 9 new tests for random selection behaviour; 572 total

Previous Release — v0.7.0

  • Review pagination: 20 posts/page with prev/next navigation
  • Bulk actions: Approve All / Reject All for the current page
  • Auto-refresh: Empty page reloads automatically when more posts exist
  • ML training snapshots: ReviewFeedback captures 12 feature fields at review time — survives queue cleanup
  • Bug fix: Stream deletion cascade fixed (Post → ReviewFeedback)

Previous Release — v0.6.0

  • Queue Lifecycle Management: Auto-delete delivered posts (7-day retention), trim excess pending with weighted random deletion, cleanup and trim-pending API endpoints
  • Production Containerization: Multi-stage build (~207 MB), non-root user, persistent volumes, automatic migrations on startup

Previous Release — v0.5.3

  • Pattern-Based User Blocking: exact, suffix, prefix, and contains matching modes — see PATTERN_BLOCKING_FEATURE.md
  • Blocklist Refresh: Apply Settings changes to the review page without losing progress

Cultural Context

"FenLiu" (分流) means "divide the flow" in Chinese, inspired by the Dujiangyan irrigation system (256 BC). This project applies the same engineering wisdom to digital content streams.

Key Resources

License

AGPL-3.0 — see LICENSE file.

Contributing

  1. Follow existing code style (ruff, comprehensive type hints)
  2. Write tests for new functionality
  3. Run nox before submitting changes
  4. Run alembic upgrade head after pulling changes with new migrations

v0.7.1 · Phase 4 In Progress · 620 tests ✅ · Forgejo

Release files for fenliu 3.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fenliu 3.0.1
File Size Uploaded
fenliu-3.0.1.tar.gz 433.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fenliu 3.0.1
File Interpreter ABI Platform
fenliu-3.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 895.4 kB

Release files / fenliu-3.0.1.tar.gz

Download URL fenliu-3.0.1.tar.gz
Size 433.3 kB
Tags Source
SHA-256 checksum
How to use checksums
394263c62b39562842a988983f4b9f8dccb314880067f94b16011883f375bec8
BLAKE2b-256 checksum
How to use checksums
84193ae105fe41d601b0ec3daecf11d7dde76b015d8a00a4f0a9d30040b02fa0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / fenliu-3.0.1-py3-none-any.whl

Download URL fenliu-3.0.1-py3-none-any.whl
Size 462.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
af31da47778df2c8b48d2e9112c6ce6334b0e05439473b8b9e4fe53bfbc628d9
BLAKE2b-256 checksum
How to use checksums
77f7a5627580033e593b271df807216d00d79b0e604db8039225c5150166cf95
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

3.1.0

2 release files

This release

3.0.1 This release

2 release files

3.0.0

2 release files

2.0.0

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.2.0

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.13.1

2 release files

0.13.0

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.10

2 release files

0.7.9

2 release files

0.7.8

2 release files

0.7.5

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.5

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.4.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page