Here's a concise, professional technical briefing for the SaveAny-Bot repository, written in the style of a senior AI engineer consultant report.
The Problem
Telegram users frequently encounter "restrict saving content" media that blocks downloads, and there is no single tool that flexibly routes files across diverse storage backends (Alist, S3, WebDAV, local, Rclone) while supporting yt-dlp integration, batch downloads, and transfer between backends. The ecosystem is fragmented: users need a unified bot that handles restrictive media, enforces per-user storage rules, and streams or transfers files without manual re-upload.
What This Does
SaveAny-Bot is a Telegram file-saving bot that persists incoming media to arbitrary backends. It supports yt-dlp for 1000+ website downloads, Aria2 for magnet/URL downloads, and custom JS parser plugins. Restrict-saving media is bypassed via the client/bot/handlers/ layer. Files are routed through api/tgfiles.go and api/handlers.go to storage backends defined in config.toml. User access is controlled through client/bot/handlers/config.go and client/bot/handlers/utils/ruleutil/rule.go. The project comprises 11 semi-independent sub-projects (client, core, pkg, storage, common, parsers, api, cmd, database) sharing a Go module structure but lacking a unified architecture.
Entry points: api/server.go starts the HTTP/server layer; cmd/run.go is the primary CLI launchpad. The api/factory.go and api/types.go files mediate backend and user configuration. storage/ implements backend handlers (local, S3, WebDAV, Alist, Rclone). client/bot/bot.go wires the Telegram client using gotd/td. yt-dlp integration lives in client/bot/handlers/dl.go and client/bot/handlers/media.go.
What it touches outside itself: Outbound network calls to Telegram API, S3/Alist/WebDAV endpoints, yt-dlp subprocess, Aria2 RPC. No external database is used; state is in-memory or configured via config.toml.
How It Is Wired
Execution starts at cmd/run.go → initializes the bot via client/bot/bot.go → registers handlers from client/bot/handlers/. Incoming updates flow through client/bot/handlers/add_task.go, parse.go, and media.go, which decide whether to queue, transform, or skip based on user rules in client/bot/handlers/config.go. The api/tgfiles.go struct mediates file metadata and storage routing. api/handlers.go dispatches to backend-specific writers in storage/. yt-dlp subprocesses are spawned from client/bot/handlers/dl.go. api/progress.go relays download progress back to Telegram.
Call graph: cmd/run.go → client/bot/bot.go (start) → client/bot/handlers/*.go (dispatch) → api/tgfiles.go (routing) → api/handlers.go (backend dispatch) → storage/*.go (write). The deepest nesting is 8 levels in client/bot/handlers/ytdlp_test.go, api/handlers_test.go, and api/tgfiles.go — control flow is hard to follow.
Hubs & blast radius: api/tgfiles.go and api/handlers.go are the primary coupling points. Any change to storage format or user rule evaluation ripples through both. common/utils/tgutil/resolve.go and api/tgfiles.go contain 236 repeated 6-line blocks across 60 files — duplicated logic for Telegram file ID resolution and path construction.
How To Use It
Setup:
# Clone verbatim
git clone https://github.com/moses-y/SaveAny-Bot
# Copy config
cp config.toml.example config.toml
# Edit config.toml with your Telegram bot token and storage definitions
# Define storages under [[storages]]: type (local/s3/webdav/alist/rclone), enable, base_path
# Define users under [[users]]: id, storages allocation, blacklist
# Build & run via Docker (recommended)
docker run -d --name saveany-bot \
-v ./config.toml:/app/config.toml \
-v ./downloads:/app/downloads \
ghcr.io/krau/saveany-bot:latest
Configuration: config.toml is the single config file. Required: Telegram token. Optional: proxy URL, storage entries, user whitelist/blacklist. See the README for the full schema.
Running it: The entry point is the Docker command above, or go run cmd/run.go locally. The bot listens on Telegram; all file routing is internal.
Real-World Use
A power user runs SaveAny-Bot to archive every IT channel video to an S3 bucket, while forwarding restricted-media files to a local NAS. They configure two storages — S3 and Local — and assign user IDs via [[users]]. When a video arrives, client/bot/handlers/media.go detects the type, api/tgfiles.go resolves the storage mapping, and storage/s3.go streams the object directly to S3 without local intermediate storage. For yt-dlp links, client/bot/handlers/dl.go invokes the subprocess, and the resulting file is routed through the same pipeline. The user never touches the filesystem; transfers are streaming where possible.
Code Health & Issues
- [HIGH/cognitive_load] Deep nesting (max indent 8) in
client/bot/handlers/ytdlp_test.go,api/handlers_test.go,api/tgfiles.go— control flow is hard to follow. Fix: flatten with early returns/guard clauses. - [HIGH/clarity] Duplicated code blocks: 236 repeated 6-line patterns across 60 files including
api/tgfiles.go,common/utils/tgutil/resolve.go,client/bot/handlers/add_task.go,client/bot/handlers/parse.go— extract shared helpers; DRY the repeated logic. - [MEDIUM/cognitive_load] High branching density in
core/tasks/directlinks/util.go— 59 branch points over 153 lines. Decompose decision-heavy logic; consider table/strategy dispatch.
SDLC observations: Tests present (22 files), CI (GitHub Actions) present, Dockerfile present, license present, lockfile present, no committed secrets. Documentation is thorough (54 doc files). The repo is a portfolio of 11 independent projects rather than a single cohesive codebase — expect inconsistent patterns and duplicated logic across sub-projects.
The Bottom Line
This is a capable, well-structured Telegram file-saver that hits the niche of restrictive-media bypass and multi-backend routing better than most alternatives. The Go code is functional and the Docker setup is solid. However, the 11-project split means you’ll encounter duplicated helpers, inconsistent naming, and nesting that slows comprehension. If you need a single, opinionated workflow — pick one storage and stick to it. If you need to extend or modify the routing logic, budget time to flatten control flow and extract the duplicated 6-line blocks scattered across the api/ and client/bot/handlers/ trees. Good for self-hosters who want yt-dlp + Aria2 + flexible storage in one bot; less ideal for teams requiring strict codebase cohesion.
Static analysis summary: 245/245 code files analyzed (Go 242, Shell 1, JavaScript 2). 18 measured findings: 2 high, 16 medium. No structural red flags beyond the above.