iyear/tdl
π₯ A Telegram toolkit written in Golang
Mixed signals β read the receipts
weakest axiscopyleft license (AGPL-3.0) β review compatibility
Has a license, tests, and CI β clean foundation to fork and modify.
Documented and popular β useful reference codebase to read through.
No critical CVEs, sane security posture β runnable as-is.
- βLast commit 6d ago
- β7 active contributors
- βAGPL-3.0 licensed
Show all 7 evidence items βShow less
- βCI configured
- βTests present
- β Concentrated ownership β top contributor handles 63% of recent commits
- β AGPL-3.0 is copyleft β check downstream compatibility
What would change the summary?
- βUse as dependency Concerns β Mixed if: relicense under MIT/Apache-2.0 (rare for established libs)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README β live-updates from the latest cached analysis.
[](https://repopilot.app/r/iyear/tdl)Paste at the top of your README.md β renders inline like a shields.io badge.
βΈPreview social card (1200Γ630)
This card auto-renders when someone shares https://repopilot.app/r/iyear/tdl on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: iyear/tdl
Generated by RepoPilot Β· 2026-05-09 Β· Source
π€Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale β STOP and ask the user to regenerate it before proceeding. - Treat the AI Β· unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/iyear/tdl shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything β but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
π―Verdict
WAIT β Mixed signals β read the receipts
- Last commit 6d ago
- 7 active contributors
- AGPL-3.0 licensed
- CI configured
- Tests present
- β Concentrated ownership β top contributor handles 63% of recent commits
- β AGPL-3.0 is copyleft β check downstream compatibility
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
β Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live iyear/tdl
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale β regenerate it at
repopilot.app/r/iyear/tdl.
What it runs against: a local clone of iyear/tdl β the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in iyear/tdl | Confirms the artifact applies here, not a fork |
| 2 | License is still AGPL-3.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit β€ 36 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of iyear/tdl. If you don't
# have one yet, run these first:
#
# git clone https://github.com/iyear/tdl.git
# cd tdl
#
# Then paste this script. Every check is read-only β no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of iyear/tdl and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "iyear/tdl(\\.git)?\\b" \\
&& ok "origin remote is iyear/tdl" \\
|| miss "origin remote is not iyear/tdl (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(AGPL-3\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"AGPL-3\\.0\"" package.json 2>/dev/null) \\
&& ok "license is AGPL-3.0" \\
|| miss "license drift β was AGPL-3.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "core/tclient/tclient.go" \\
&& ok "core/tclient/tclient.go" \\
|| miss "missing critical file: core/tclient/tclient.go"
test -f "core/downloader/downloader.go" \\
&& ok "core/downloader/downloader.go" \\
|| miss "missing critical file: core/downloader/downloader.go"
test -f "core/uploader/uploader.go" \\
&& ok "core/uploader/uploader.go" \\
|| miss "missing critical file: core/uploader/uploader.go"
test -f "core/dcpool/dcpool.go" \\
&& ok "core/dcpool/dcpool.go" \\
|| miss "missing critical file: core/dcpool/dcpool.go"
test -f "app/login/login.go" \\
&& ok "app/login/login.go" \\
|| miss "missing critical file: app/login/login.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 36 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~6d)"
else
miss "last commit was $days_since_last days ago β artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) β safe to trust"
else
echo "artifact has $fail stale claim(s) β regenerate at https://repopilot.app/r/iyear/tdl"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
β‘TL;DR
tdl is a high-performance Telegram client toolkit written in Go that downloads files from chats (including protected ones), forwards messages with intelligent fallback routing, uploads files, and exports chat data to JSON. It's faster than official Telegram clients by leveraging the gotd/td TDLib bindings and saturates available bandwidth for bulk operations. Modular command structure: app/ directory contains feature domains (dl/, up/, forward/, chat/, login/, migrate/) each with independent iter.go (streaming logic), elem.go (data structures), and progress.go (UI feedback). app/internal/tctx/ provides shared Telegram context. CLI entry points likely in cmd/ (not shown in file list but inferred from Makefile presence).
π₯Who it's for
DevOps engineers, data archivists, and automation specialists who need to bulk-download Telegram media and messages, migrate chat data, or programmatically interact with Telegram through a CLI without the overhead of the official client.
π±Maturity & risk
Production-ready and actively maintained. The project has comprehensive GitHub Actions workflows (master.yml, release.yml, docker.yml), uses golangci-lint for code quality, and includes end-to-end tests (e.g., app/dl/iter_test.go). Single maintainer (@iyear) with active releases and sponsor support; Go 1.25.8 baseline indicates modern maintenance.
Low risk for core download/upload operations, but moderate dependency risk: 6 direct dependencies (gotd/td, gotd/contrib, samber/lo, yaml, backoff, mimetype) with deep transitive deps (~40 indirect). The single-maintainer model (iyear) is a concentration risk for critical bugs. AGPL-3.0 license may restrict commercial use without code disclosure.
Active areas of work
Active release pipeline via .github/workflows/release.yml and Docker image builds (docker.yml). Dependabot auto-updates configured (dependabot-fix.yml) suggesting regular dependency maintenance. The presence of serve.go.tmpl (app/dl/) suggests recent work on HTTP serving features. No explicit issue data provided, but docs.yml indicates documentation is kept current.
πGet running
git clone https://github.com/iyear/tdl.git
cd tdl
go mod download
make build # inferred from Makefile presence
./tdl --help
Daily commands:
Build: make build (Makefile exists but not shown; inferred from GitHub workflows). Run: ./tdl [command] [flags] (CLI-driven, see .github/workflows/master.yml for CI commands). Local testing: go test ./... for iter_test.go and likely other test files.
πΊοΈMap of the codebase
core/tclient/tclient.goβ Core Telegram client wrapper that abstracts gotd/td library; all Telegram API interactions funnel through herecore/downloader/downloader.goβ Primary download orchestrator managing bandwidth, concurrency, and media retrieval from Telegram; entry point for dl commandcore/uploader/uploader.goβ File upload handler managing streaming uploads to Telegram; critical for up command flowcore/dcpool/dcpool.goβ Data center connection pooling and middleware orchestration; manages low-level Telegram transportapp/login/login.goβ Authentication logic handling phone/code/QR flows; required for all subsequent operationscmd/root.goβ Cobra CLI root command initialization and global flag setup; entry point for CLI executioncore/storage/storage.goβ Session persistence and state management; maintains user authentication and peer data across runs
π οΈHow to make changes
Add a new download format filter
- Define filter logic in core/downloader/downloader.go by adding a FilterFn type and applying it to iter.Next() (
core/downloader/downloader.go) - Expose filter option in app/dl/dl.go Download() function signature (
app/dl/dl.go) - Add CLI flag in cmd/dl.go and parse it into the app layer call (
cmd/dl.go)
Add a new chat export format (e.g., CSV)
- Create export enum in app/chat/export_enum.go with new format constant (
app/chat/export_enum.go) - Implement format marshaler in app/chat/export.go Export() function with conditional encoding (
app/chat/export.go) - Add CLI --format flag in cmd/chat.go and route to app layer (
cmd/chat.go)
Add a new authentication method
- Create auth flow function in app/login/login.go following QR/Phone/Code pattern (
app/login/login.go) - Add login enum variant in app/login/login_enum.go (
app/login/login_enum.go) - Add CLI flag in cmd/login.go and route to new auth handler (
cmd/login.go) - Store session in core/storage/storage.go if state differs from standard (
core/storage/storage.go)
Add a new middleware for request handling
- Create middleware file in core/middlewares/{name}/ with func(ctx context.Context, req T, invoker gotd.Invoker) (R, error) (
core/middlewares/recovery/recovery.go) - Register middleware in core/dcpool/middlewares.go in the middleware chain (
core/dcpool/middlewares.go)
π§Why these technologies
- Go 1.25.8 β Concurrent download/upload, lightweight binary distribution, fast compilation for cross-platform targets
- gotd/td (Telegram MTProto client library) β Pure Go Telegram client without TDLib C dependency; allows custom middleware and connection pooling for performance
- Cobra (CLI framework) β Mature flag parsing and subcommand routing; integrates well with Go's idiomatic patterns
- uber/zap (structured logging) β High-performance structured logging with context propagation; minimal overhead during heavy I/O
- SQLite (session storage) β Single-file persistence of Telegram sessions without external dependencies; portable across platforms
- cenkalti/backoff (retry logic) β Exponential backoff with jitter for resilient Telegram API calls under rate-limiting
βοΈTrade-offs already made
-
Direct gotd/td usage instead of TDLib wrapper
- Why: Avoid C dependency for cross-platform binary distribution and enable custom networking (connectproxy for proxy support)
- Consequence: Must manually handle Telegram MTProto specifics (DC pooling, middleware), but gains transparency and control
-
Concurrent download with bandwidth awareness instead of simple queue
- Why: Maximize throughput to saturate user's connection (advertised feature: 'take up all your bandwidth')
- Consequence: Increased complexity in progress tracking and memory buffering; requires careful chunking to avoid OOM on large files
-
SQLite for session storage instead of in-memory
- Why: Persist sessions across CLI invocations; avoid re-authentication on every run
- Consequence: Filesystem I/O overhead, but acceptable for CLI tool with sparse invocation frequency
-
Middleware-based architecture for retry/recovery/takeout
- Why: Cleanly separate cross-cutting concerns from core business logic; reuse across download/upload/forward
- Consequence: Extra abstraction layer; requires understanding gotd invoker pattern to add new middleware
π«Non-goals (don't propose these)
- Real-time chat UI (CLI-only tool)
- End-to-end encrypted chat support (only public chats and user's own accounts)
- Windows subsystem for Linux native support (Go binary works on WSL but no special WSL integration)
- Automatic upload scheduling or daemon mode (single-run operations via CLI)
- Streaming media transcoding (media downloaded as-is from Telegram)
πͺ€Traps & gotchas
No explicit .env or config schema found in file list: auth likely stored in home directory (typical for Telegram clients; check docs for schema). AGPL-3.0 license: using or modifying tdl in closed-source projects requires making your code public or obtaining a waiver. Dependabot auto-merge: security patches may land unexpectedly; watch gotd/td closely for breaking API changes. go.mod in repo root indicates single module: no internal dependency graphs to worry about, but package structure implies CLI binary in undisclosed location (likely cmd/tdl or cmd/main).
ποΈArchitecture
π‘Concepts to learn
- MTProto (Mobile Transport Protocol) β The Telegram wire protocol that gotd/td implements; understanding MTProto encryption and message framing explains why tdl can bypass rate limits and access protected chats
- Iterator Pattern (Streaming) β tdl uses iter.go files throughout (dl/iter.go, forward/iter.go, up/iter.go) to stream large datasets without loading into memory; critical for handling massive chat exports
- Context-based Concurrency Control β app/internal/tctx wraps context.Context for Telegram session management and cancellation; Go's context package is essential for graceful shutdown and timeout handling in distributed operations
- Exponential Backoff with Jitter β cenkalti/backoff/v4 is imported for retry logic; prevents thundering herd when download workers hit rate limits, enabling tdl to 'take up all your bandwidth' without crashing the API
- MIME Type Detection β gabriel-vasile/mimetype is used (likely in elem.go files) to classify downloaded media without relying on file extensions, enabling correct export metadata and re-upload categorization
- Structured Logging with Correlation IDs β uber/zap is the logging framework; combined with tctx, this enables tracing download/forward/upload operations across goroutinesβessential for debugging multi-worker bulk operations
- Message Fallback Routing β app/forward/forward.go implements intelligent retry and fallback when forwarding to protected chats or closed groups; understanding Telegram's forward API constraints is key to extending this feature
πRelated repos
gotd/tdβ Direct upstream dependency providing Telegram MTProto bindingsβtdl wraps this library for CLI ergonomicsgotd/contribβ Sibling library in the gotd ecosystem providing retry, rate-limit, and streaming helpers that tdl depends ontelegramdesktop/tdesktopβ Official Telegram client; tdl attempts to match or exceed its download/forward performance while offering CLI automationiyear/connectproxyβ Proxy helper library by the same maintainer; used in tdl for connection routing through custom proxiesyapingcat/gomediaβ Media parsing library vendored in tdl dependencies; handles codec detection for uploaded/downloaded files
πͺPR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for app/dl/iter.go download iterator
The iter.go file handles critical download iteration logic but only has iter_test.go with limited coverage. Add comprehensive integration tests covering edge cases like resumed downloads, corrupted partial files, concurrent downloads, and network interruptions. This is high-value because download reliability is core to tdl's value proposition.
- [ ] Expand app/dl/iter_test.go with table-driven tests for different file sizes and chunk combinations
- [ ] Add mock Telegram connection tests simulating network failures and timeouts
- [ ] Test concurrent download scenarios with multiple files to verify progress.go synchronization
- [ ] Add regression tests for known issues around incomplete file handling in iter.go
Add CI workflow for cross-platform binary testing
The repo has docker.yml, release.yml, and master.yml workflows, but lacks a workflow specifically testing built binaries on Windows, macOS, and Linux before release. Given tdl is a CLI tool with platform-specific concerns (file paths, terminal colors, websocket handling via coder/websocket), this prevents catching platform-specific bugs early.
- [ ] Create .github/workflows/cross-platform-test.yml that builds binaries for linux/amd64, darwin/arm64, darwin/amd64, windows/amd64
- [ ] Run basic smoke tests on each platform (login command, --version, help output)
- [ ] Test app/dl/serve.go template rendering (serve.go.tmpl) on each OS to catch path separator issues
- [ ] Ensure the workflow runs on pull requests and reports failures
Add unit tests for app/chat/export_enum.go and ls_enum.go enum generation
The codebase has export_enum.go and ls_enum.go files that appear to be code-generated enum utilities (similar pattern to cmd/gen.go), but there are no tests validating that enum mappings are correct or that export/list operations handle all enum variants. Missing test coverage here could cause silent failures in message export and chat listing.
- [ ] Create app/chat/export_enum_test.go with tests for all export format enums and their string representations
- [ ] Create app/chat/ls_enum_test.go validating all ls filter enums match Telegram API expectations
- [ ] Add test cases validating enum round-trip conversions (string β enum β string)
- [ ] Add tests ensuring missing or misspelled enum values fail with clear error messages
πΏGood first issues
- Add integration tests for app/forward/iter.go similar to app/dl/iter_test.goβcurrently only download iteration is tested; forward and upload iterators lack test coverage
- Create OpenAPI/Swagger docs from existing CLI enums (app/dl/dl_enum.go, app/chat/ls_enum.go, app/login/login_enum.go) to auto-generate command referenceβcurrently only markdown docs exist
- Add graceful shutdown hooks to app/dl/serve.go (HTTP server template)βno context cancellation or timeout handling visible in the .tmpl file
βTop contributors
Click to expand
Top contributors
- @dependabot[bot] β 63 commits
- @iyear β 25 commits
- @XMLHexagram β 6 commits
- @L9Lenny β 2 commits
- @EzekialSA β 2 commits
πRecent commits
Click to expand
Recent commits
d4c59b1β chore(deps): bump github.com/go-playground/validator/v10 (#1206) (dependabot[bot])6d1b069β chore(deps): bump actions/configure-pages from 5 to 6 (#1205) (dependabot[bot])6b17e53β chore(deps): bump tdl submodules to v0.20.2 (iyear)b54c8c9β chore(deps): bump tdl/core to v0.20.2 (iyear)75b4160β chore(deps): update gotd/td to v0.140.0 (#1204) (iyear)9e814f0β chore(deps): bump golang.org/x/net from 0.51.0 to 0.52.0 in /core (#1191) (dependabot[bot])d61eed8β chore(deps): bump golang.org/x/net from 0.51.0 to 0.52.0 (#1190) (dependabot[bot])8964e91β chore(deps): bump github.com/klauspost/compress from 1.18.4 to 1.18.5 (#1200) (dependabot[bot])4139ba4β chore(deps): bump actions/deploy-pages from 4 to 5 (#1202) (dependabot[bot])9ba804dβ chore(deps): bump github.com/fatih/color from 1.18.0 to 1.19.0 (#1198) (dependabot[bot])
πSecurity observations
- High Β· Outdated Go Version in Dockerfile β
Dockerfile, line 2. The Dockerfile uses Go 1.25-alpine which does not exist. Go versions follow semantic versioning (1.21, 1.22, 1.23, etc.). This suggests the Dockerfile may not build correctly or uses an invalid version tag. Using non-existent or improperly pinned versions can lead to unexpected behavior and security patches being missed. Fix: Use a valid, supported Go version such as golang:1.23-alpine or golang:1.24-alpine. Pin to a specific patch version for reproducibility (e.g., golang:1.23.4-alpine). - High Β· Outdated Go Module Version β
core/go.mod, line 2. The core/go.mod file declares 'go 1.25.8' which is not a valid Go version. Valid versions are 1.21.x, 1.22.x, 1.23.x, etc. This misconfiguration could cause build failures and module resolution issues across the project. Fix: Update to a valid Go version such as 'go 1.23' or 'go 1.24'. Ensure consistency between Dockerfile and go.mod declarations. - Medium Β· Outdated Dependency: golang.org/x/crypto β
core/go.mod, indirect dependency. The dependency golang.org/x/crypto is pinned to v0.49.0, which is significantly outdated. Current versions are v0.24.0+. Older cryptographic libraries may contain known vulnerabilities and lack security patches. Fix: Update golang.org/x/crypto to the latest stable version. Run 'go get -u golang.org/x/crypto' and test thoroughly. - Medium Β· Outdated Dependency: golang.org/x/net β
core/go.mod, line 11. The dependency golang.org/x/net is pinned to v0.52.0. While relatively recent, network libraries should be kept current to address potential protocol-level vulnerabilities and DoS issues. Fix: Review and update golang.org/x/net to the latest version. Run 'go get -u golang.org/x/net' and validate no breaking changes affect the application. - Medium Β· Missing Security Headers in Docker Configuration β
Dockerfile, line 16. The Dockerfile uses 'alpine:latest' as the base image without pinning to a specific version. This could result in pulling a different image on subsequent builds, potentially including security patches or regressions. Additionally, there are no security-related configurations (e.g., read-only filesystem, user privilege dropping). Fix: Pin alpine to a specific version (e.g., alpine:3.19). Add security best practices: use 'RUN adduser -D -u 1000 tdl' to create a non-root user and 'USER tdl' to drop privileges. - Medium Β· Potential Sensitive Data in Logs β
core/logctx, app modules. The application uses logging libraries (go.uber.org/zap) and handles Telegram API interactions. There is a risk of logging sensitive data (authentication tokens, file contents, user IDs) if logging is misconfigured. Review of actual logging implementation was not possible from file names alone. Fix: Implement structured logging with careful sanitization. Ensure no sensitive credentials, tokens, or personal user data are logged. Use log levels appropriately (DEBUG for development only). - Low Β· Build-Time Version Injection via LDFLAGS β
Dockerfile, lines 14-15. The Dockerfile injects version information via ldflags during build. While this is a standard practice, ensure the VERSION, COMMIT, and COMMIT_DATE variables come from trusted CI/CD sources only and cannot be manipulated by unauthorized users. Fix: Ensure CI/CD pipeline validates and controls these build-time variables. Document the source of truth for VERSION and COMMIT variables. - Low Β· Missing HEALTHCHECK in Dockerfile β
Dockerfile. The Dockerfile does not define a HEALTHCHECK instruction, which is important for orchestration platforms like Kubernetes or Docker Compose to monitor application health and restart unhealthy containers. Fix: Add a HEALTHCHECK instruction that
LLM-derived; treat as a starting point, not a security audit.
πWhere to read next
- Open issues β current backlog
- Recent PRs β what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals β see the live page for receipts. Re-run on a new commit to refresh.