A chat widget for your website. Reply to visitors from Telegram or a small web inbox.
This is the single-owner, self-hosted version. One deployment connects to your bot and support chat. No signup service or multi-customer platform.
Text, images, video, audio and documents are supported, with separate visitor conversations and Open / Pending / Resolved statuses. Runs on Cloudflare Workers + D1. Plain HTML, CSS and JavaScript on the frontend.
Use Node 24 and npm.
git clone https://github.com/oladapodev/webtel.git
cd webtel
npm ci
cp .dev.vars.example .dev.varsSet ADMIN_TOKEN in .dev.vars to a random secret of at least 32 characters. You can generate one with openssl rand -hex 32. Keep this file private.
npm run db:local
npm run devOpen http://localhost:8799 and sign in with that token. The dashboard works locally without Telegram. The widget appears when you use the installation preview.
- Create a dedicated bot with @BotFather, then open your bot and press Start.
- Fill in
.dev.vars:TELEGRAM_BOT_TOKEN,TELEGRAM_CHAT_ID, and a randomTELEGRAM_WEBHOOK_SECRET(openssl rand -hex 32). For a private chat, your Telegram user ID is the chat ID. You can find it with @userinfobot. SetTELEGRAM_AGENT_IDSto the Telegram user IDs allowed to reply, separated by commas. - Create your own database:
npx wrangler login
npx wrangler d1 create webtelReplace the placeholder database_id in wrangler.jsonc with the returned ID. Choose a Worker name that is not already used by another app in your account. In vars.SITES, add your website's exact origin and your Worker URL. For example, https://example.com, without a path. www.example.com is a different origin.
npm run db:remote
npx wrangler secret put ADMIN_TOKEN
npx wrangler secret put TELEGRAM_BOT_TOKEN
npx wrangler secret put TELEGRAM_CHAT_ID
npx wrangler secret put TELEGRAM_WEBHOOK_SECRET
npx wrangler secret put TELEGRAM_AGENT_IDS
npm run deploy
WEBTEL_URL=https://YOUR_WORKER.workers.dev npm run telegram:setupEnter the same values from .dev.vars when Wrangler prompts. Telegram needs a public HTTPS webhook, so local development alone will not receive replies. Don't connect a bot that another application is using.
Add this before </body> on your website, using your deployed URL:
<script src="https://YOUR_WORKER.workers.dev/widget.js" data-site="default" defer></script>Send a test message. In Telegram, tap Connect, wait for confirmation, then reply normally. Switch changes the active conversation. Disconnect stops routing your messages. Replying to a specific bot message still works.
Copy the block below into your coding agent. It can set up a standalone deployment or adapt the widget to your app.
Set up https://github.com/oladapodev/webtel for my website.
Read the README, wrangler.jsonc, src/index.ts, public/widget.js and
scripts/telegram-setup.mjs first. This is the single-owner version, not
a hosted signup platform. Keep the implementation simple.
1. Clone the repo if needed. Run npm ci, npm run check and npm test.
2. Inspect my app before changing it. Ask for my website origin and whether
I want a separate Worker or integration into my existing Cloudflare app.
3. Create .dev.vars from the example only if it does not already exist.
Generate strong admin/webhook secrets. Let me supply my bot token,
support chat ID and allowed Telegram user IDs securely. Never put
secrets in source code, the widget snippet, Git, or your final reply.
4. Run npm run db:local and npm run dev. Open the dashboard and check it.
5. With my approval, create a new D1 database, set its ID and allowed origins,
upload secrets, apply remote migrations, and deploy. Do not overwrite
an existing Worker/database or replace another app's bot webhook.
6. Register the webhook with WEBTEL_URL=<deployed URL> npm run telegram:setup.
Add the widget script to my site's HTML, or load it once from the app's
root layout if it is an SPA. Use my own Worker URL, not someone else's.
7. Test the real flow: website message -> Telegram Connect -> normal reply
-> website. Check two independent visitors don't receive each other's
replies, try an image in both directions, and check status changes.
8. If adapting the backend, preserve session-token authentication, exact
origin checks, agent restrictions, deduplication and protected media
downloads. Run the tests again. Tell me what you verified and what is
blocked by missing credentials or access. Don't claim an untested deploy.
- The widget polls every 3 seconds. It does not use WebSockets.
- Files are limited to 10 MiB and stored by Telegram. Downloads go through authenticated Worker endpoints. Telegram may recompress photos or treat some media as documents.
- Outbound text delivery retries, so duplicates are possible after an uncertain network failure.
- Conversations are stored in D1, files in Telegram, and visitor session credentials in browser local storage. There is no automatic chat deletion. Set a retention policy for your site.
- Cloudflare and Telegram quotas still apply. Free-tier availability is not a promise of unlimited free hosting.
npm run check checks TypeScript. npm test runs Worker/D1 integration tests with Telegram mocked. Real bot delivery still needs the end-to-end check above.
MIT licensed.