DigiBot is a personal, invite-only Telegram media bot on Cloudflare. Send it a public link, or use the companion browser extension, and you get the video, audio, clip or transcript in your Telegram chat.
DigiBot uses a TypeScript Worker and two Python Containers: the downloader Container and the transcription Container. Delivery is idempotent. Network egress is safe from server-side request forgery (SSRF). The project has about 1,300 automated tests. Engineering design links each design point to its tests.
| Message | Result |
|---|---|
URL |
Video download (up to 1080p) |
/video URL [from 12:00 to 17:00] |
Choose 1080p, 720p, 480p or 360p. Optional trim. |
/audio URL [m4a|mp3] [first 5 minutes] |
M4A or MP3 audio. Optional trim. |
/clips URL from 00:00 to 00:02; from 00:03 for 2 seconds |
2–3 clips in one Telegram album |
/playlist URL [1-5], /channel URL [1-5] |
The first items of a YouTube playlist or channel |
/transcript URL |
Markdown transcript with timestamps (Whisper small, sources up to 15 minutes) |
/captions URL [language] |
Publisher or automatic captions as Markdown |
/search phrase as a reply to a transcript |
Matching passages with timestamps |
Reply /audio or /video to a Telegram file |
Convert a file up to 20 MB |
/queue, /status, /stats, /activity |
Queue positions, outcomes and recent activity |
Access and delivery:
- DigiBot works only in private chats.
- Two allowlisted owners can use DigiBot. Users that the owners invite for the extension features can also use DigiBot.
- A Telegram Mini App shows history and statistics.
- If a file is larger than the Telegram upload limit, you get a private, signed link. The link expires after one hour.
flowchart LR
U["Telegram user"] -->|"webhook"| W["Cloudflare Worker"]
L["Provenance Lens extension"] -->|"connected-media API"| W
W --> D1[("D1")]
W --> WF["Workflows"]
WF --> C["downloader and transcription Containers<br/>FastAPI, yt-dlp, FFmpeg, whisper.cpp"]
C -->|"oversize file"| R2[("R2")]
W -->|"signed link"| R2
W -->|"notices and status"| TG
W -.->|"service binding, not in repo"| V["Verifier Worker"]
C -->|"file or link"| TG["Telegram Bot API"]
TG --> U
- The Worker checks the webhook secret, the user allowlist and the URL.
- In one transaction, the Worker admits the request into a durable D1 queue.
- A Workflow, keyed by the job ID, runs preparation steps that can retry.
- One final delivery step follows. This step does not retry.
- The downloader Container probes and downloads the source with yt-dlp.
- It converts and verifies the result with FFmpeg and FFprobe.
- For transcripts, the transcription Container runs whisper.cpp.
- The result goes directly to Telegram.
- If a file is too large for Telegram, it goes to a private R2 bucket.
- The user then gets a signed link that expires.
The connected-media API pairs DigiBot with the Provenance Lens browser extension (Lens). Lens can send a page link to DigiBot. Lens can also check images for Content Credentials. For more information, see docs/architecture.md and docs/integration.md.
- Cloudflare: Workers (TypeScript), Workflows, D1 (SQLite), R2, Containers (through Durable Object bindings), Wrangler
- Containers: Python 3.12, FastAPI, Uvicorn, Pydantic, HTTPX, boto3
- Media tools: yt-dlp (with yt-dlp-ejs and Deno), FFmpeg and FFprobe, whisper.cpp
- Telegram: Bot API (webhooks, multipart uploads, media groups, inline keyboards), Mini Apps
- Testing and quality: Vitest with Miniflare/workerd, pytest, mypy (strict), Ruff, ESLint, typescript-eslint
- Tooling: pnpm workspaces, uv, Docker (linux/amd64), GitHub Actions
One D1 transaction reserves the Telegram update ID, the user's allowances and the queue entry. In a test, 100 concurrent duplicate updates admit exactly one job. Workflow steps and Container preparation can repeat without a second download or send.
Tests: recovery-d1, workflow-replay.
Telegram has no upload idempotency key. For this reason, the final send never retries automatically. DigiBot records an ambiguous send as unknown. A recovery job runs every minute. It uses generation-fenced leases and repairs D1 only from confirmed receipts. /reconcile checks the file hash before it records a lost receipt.
Tests: dispatch, integration-telegram, test_telegram.
DigiBot uses an exact host allowlist with IDNA (Internationalized Domain Names for Applications) normalization. DigiBot rejects URLs with credentials. DigiBot also rejects URLs that point to private, loopback, link-local or metadata addresses. yt-dlp connects through a local egress proxy. The proxy resolves each host and connects only to a public IP. As a result, a DNS change cannot reach an internal address.
Tests: test_egress_proxy, test_security, url.
DigiBot limits duration (2 h), source size (500 MB) and Telegram payload (49 MB, with a re-encode ladder). Subprocesses use argv arrays, absolute deadlines, output caps and process-group kill. Each job has its own workspace. The downloader Container removes the workspace after delivery.
Tests: test_workspace_process, test_direct_media.
Each user has an hourly cap and at most five unfinished jobs. Downloads and transcripts use separate FIFO (first in, first out) lanes. Searches and playlist lookups have their own cooldowns.
Tests: recovery-d1, webhook.
Invitations are single-use. Pairing compares codes. Refresh tokens rotate, with a fixed 30-day session limit. The connected-media API uses an exact chrome-extension:// origin allowlist. Invited accounts get a smaller link-download quota.
Tests: integration-auth, integration-link.
DigiBot stores source URLs with AES-GCM encryption, and only while a job needs them. Download links have an HMAC signature. Logs contain job IDs, hostnames and stable error codes. Logs never contain URLs, tokens or Telegram IDs.
- The Worker has 798 Vitest tests in 52 files. The D1 tests run against a local D1 database in Miniflare/workerd.
- 498 pytest tests cover the Python code of the Containers. The ASR (automatic speech recognition) scorer has its own self-check.
- Two more pytest tests are opt-in and do not run by default: a live media check and a local whisper.cpp engine check.
- The tests use fakes for Telegram, R2, Workflows and the Containers. The tests make no network calls to media sites.
- CI also runs ESLint,
tsc, mypy, Ruff, a Wrangler dry-run build, Container image builds, dependency audits and a secret scan.
docs/measurements has dated measurements of latency, D1 query costs, encoding and transcription accuracy.
| Path | Contents |
|---|---|
apps/cloudflare-worker/ |
Webhook, admission, Workflows, Mini App, connected-media API, D1 migrations, tests |
apps/downloader-container/ |
FastAPI media and transcription service, Dockerfiles, tests |
scripts/ |
Local development, Telegram webhook setup, dependency updates, secret scan |
tools/ |
Offline ASR accuracy scorer and its self-check |
docs/ |
Architecture, security, integration, development, troubleshooting, measurements |
To run the checks, you need:
- Node.js 22.22.2 or newer
- pnpm through Corepack (
package.jsonpins the pnpm version) - Python 3.12
- uv
To deploy your own copy, you also need:
- A Telegram bot from BotFather
- A Cloudflare account on the Workers Paid plan (Containers need this plan)
- Docker
Run these commands from the repository root:
corepack enable
pnpm install --frozen-lockfile
pnpm check # lint, typecheck, Worker tests, Wrangler dry-run build, secret scan
cd apps/downloader-container
uv sync --frozen --all-groups
uv run ruff check .
uv run mypy src
uv run pytest -q
uv run python ../../tools/test_asr_eval.pypnpm check:py runs the same Python steps from the repository root. The
development guide explains local Worker development and
dependency updates.
- Create a Telegram bot with BotFather.
- In your Cloudflare account, create a D1 database and a private R2 bucket. Containers need the Workers Paid plan.
- Replace the placeholders in
apps/cloudflare-worker/wrangler.jsonc:database_id,R2_ENDPOINT,PUBLIC_WORKER_BASE_URLandINTEGRATION_ALLOWED_ORIGINS. - From
apps/cloudflare-worker, set each of these secrets withpnpm wrangler secret put <NAME>:TELEGRAM_BOT_TOKENTELEGRAM_WEBHOOK_SECRETINTERNAL_CONTAINER_SECRETALLOWED_TELEGRAM_USER_IDSDOWNLOAD_LINK_HMAC_SECRETR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEY
- Set
ALLOWED_TELEGRAM_USER_IDSto exactly two distinct numeric Telegram user IDs, separated by a comma. Any other value disables DigiBot. - Start Docker.
- For the first deploy, run
pnpm wrangler d1 migrations apply DB --remotefromapps/cloudflare-worker. - Run
pnpm wrangler deployfrom the same folder. This first deploy builds both Container images. Use--containers-rollout noneonly for later Worker-only updates. - Register the webhook with
scripts/set-telegram-webhook.ts.
Keep every secret out of tracked files.
Connected mode calls a separate verifier Worker through the
PROVENANCE_VERIFIER service binding. The verifier Worker is not in this
repository. If you do not have a verifier Worker, do these steps:
- Set
INTEGRATION_ENABLEDtofalse. - Point the
PROVENANCE_VERIFIERbinding at your own Worker, or remove the binding.
The Worker README has the exact commands.
This repository is a cleaned public snapshot of DigiBot 5.0.0, taken from a private production repository. The snapshot contains the full Worker and Container source, migrations, tests and CI.
- Placeholders replace the real Cloudflare resource IDs, hostnames, extension IDs and the verifier service name.
- The snapshot does not include internal tooling: the deploy workflow, release and provisioning scripts, operations runbooks, the changelog and the Git history.
- This snapshot has no provider-specific direct-media resolver. yt-dlp handles every source, including TikTok and X.
Use DigiBot only for media you own, media under an open license, or media you have permission to download. It does not bypass DRM, paywalls, logins, CAPTCHAs or private-media controls. You are responsible for following each platform's terms and copyright law. Report vulnerabilities as described in SECURITY.md.
Blimp3 develops DigiBot with Claude Code, Anthropic's AI coding assistant. Claude Code is a development partner for code, tests, reviews and docs.
MIT © 2026 Blimp3. Third-party dependencies keep their own licenses.