When a download completes but the user clicks Cancel at the same time, handleDownloadCompletion's Telegram calls used the cancelled download context and failed. Use context.Background() for all notification calls so the file is always delivered regardless of download state.
uptodownbot
Telegram bot for downloading media from any site yt-dlp supports.
Features
- Download audio, video, or combined audio+video from YouTube, Spotify, SoundCloud, Vimeo, and hundreds more
- Search support: search YouTube/YouTube Music by name when no URL is provided
- DRM fallback: automatically searches YouTube when Spotify/DRM-protected content is detected
- Thumbnail preview with metadata (title, uploader, duration) before download
- Audio-only sites (Spotify, SoundCloud, YouTube Music, etc.) auto-detect and skip to audio quality selection
- Multi-step format selection: quality, secondary format (audio/video combine), language
- Download progress updates (with speed and ETA)
- Cancel downloads at any time (terminates yt-dlp process)
- Per-user quota system (daily, weekly, monthly; 0 = unlimited)
- User preferences (default audio/video quality, language)
- Download history with pagination
- Configurable file size limit (default 50 MB)
- Cookies support for age-restricted content
- Proxy support via HTTP_PROXY/HTTPS_PROXY env vars
- Webhook and polling modes
- SQLite database for persistence
- yt-dlp plugin support —
hanime-pluginbundled in Docker image
Commands
/start- Show main menu/download- Prompt for a download URL/settings- Open settings/help- Show help text/history- View download history
You can also send a URL directly to start downloading immediately.
Configuration
Copy .env.example to .env and configure:
| Variable | Default | Description |
|---|---|---|
BOT_TOKEN |
(required) | Telegram Bot API token |
HTTP_PROXY |
empty | Outbound HTTP proxy |
HTTPS_PROXY |
empty | Outbound HTTPS proxy |
DB_PATH |
data/uptodown.db |
SQLite database path |
DOWNLOAD_DIR |
/tmp/uptodown |
Temporary download directory |
MAX_FILE_SIZE_MB |
50 |
Maximum upload file size in MB |
COOKIES_FILE |
empty | Path to cookies.txt for auth |
TZ |
UTC |
Timezone |
BOT_ALLOWED_USERS |
empty | Comma-separated allowed user IDs |
BOT_WEBHOOK_URL |
empty | Webhook URL (omit for polling) |
BOT_LISTEN |
:8080 |
Webhook listen address |
BOT_TLS_CERT |
empty | TLS certificate path |
BOT_TLS_KEY |
empty | TLS key path |
Running
Polling mode (default)
cp .env.example .env
# edit .env with your BOT_TOKEN
make run-dev
Webhook mode
export BOT_WEBHOOK_URL=https://your.domain.com/webhook
make run-dev
Docker
make docker-run
The Docker image bundles the hanime-plugin and the Deno JS runtime for sites like
hanime.tv, hstream.moe, and hentaihaven.com. See yt-dlp plugins for details.
Local (non-Docker)
If running outside Docker, the bot uses whatever yt-dlp is in your PATH. Install plugins and a JS runtime as described below under yt-dlp plugins.
yt-dlp plugins
Some sites require yt-dlp plugins or a JavaScript runtime to work. The Docker image bundles these automatically; for local installs you need to set them up manually.
hanime-plugin
Adds support for hanime.tv, hstream.moe, hentaihaven.com and others.
pip install hanime-plugin
The plugin requires a JavaScript runtime. Deno is recommended:
# Linux/macOS
curl -fsSL https://deno.land/install.sh | sh
# Add to your shell config:
export PATH="$HOME/.deno/bin:$PATH"
Adding other plugins
yt-dlp plugins are Python packages installed via pip. Most work without additional runtimes. Install any pip-installable yt-dlp plugin with:
pip install <plugin-name>
Development
make build # build binary
make test # run tests
make fmt # format code
make vet # vet code
make check # fmt + vet