Automation · 2026
A Telegram bot that relays text between two people and translates each message into the language the recipient chose.
Year
Type
Role
Tech Stack
LinguaRelay lets two people who speak different languages talk through a Telegram bot. Telegram bots cannot read private chats between users, so each person writes to the bot in their own chat, and the backend translates the text into the other person's preferred language and sends it from the bot, with the original text added if they want it. It supports 16 translation languages, a Turkish and English bot interface, invite links with approval from both sides, and leave, block, report and account deletion flows. It is aimed at friends, family, travelers and colleagues who talk one-to-one across a language barrier.
The webhook only verifies, validates and commits: the Telegram update, the message and an outbox job are written in a single PostgreSQL transaction, and translation and delivery happen afterwards in a separate worker. Duplicate webhook deliveries are ignored through a unique update ID, and the worker claims jobs with FOR UPDATE SKIP LOCKED, keeps its lease alive with a heartbeat and retries with capped exponential backoff and full jitter. Before translation, links, mentions, code, emails and emoji are replaced with nonce placeholders that are checked when the text comes back. Long output is split into numbered chunks that stay under Telegram's 4096-character limit.
/invite creates a single-use deep link that is stored only as a digest. The invitee claims it, and the inviter has to approve before the conversation becomes active.
Recipients pick an incoming language from 16 supported BCP 47 tags, and senders can use auto-detection or set a fixed source language. /original adds the source text below each translation.
13 commands, including /start, /chats, /language, /settings and /privacy, are registered with Turkish and English descriptions. Onboarding walks users through a privacy notice, the interface language and the incoming language.
Leave, block/unblock, report and delete-account flows, with reports capped at 5 per day by default. Account erasure runs as a background job and is safe to repeat.
Five outbox job types (translate, deliver, bot response, account erasure, retention cleanup), each with a dedupe key, an attempt cap and a dead-letter state. Delivery rechecks blocks and conversation status before each chunk is sent.
An optional read-only /admin page behind Basic auth shows growth, the activation funnel, language and source breakdowns, queue state and errors without message content. It never displays message text, translations or Telegram IDs.
The Fastify 5 webhook checks the X-Telegram-Bot-Api-Secret-Token header with a constant-time comparison, applies a 64 KiB default body limit and responds after one database commit. The code is a modular monolith split into domain, application and adapter layers, and ESLint stops inner layers from importing grammY.
PostgreSQL 18 with Prisma 7: 16 models and 7 committed migrations, with unique keys on Telegram update IDs, outbox dedupe keys and translations. Advisory locks cover invite pairing, message admission and a provider concurrency cap that holds across replicas.
The worker renews its lease every third of the lease period and retries with full-jitter backoff (1 s base, 5 min cap by default), respecting Retry-After when the provider sends it. Azure responses are validated with Zod, and each error is classified as retryable, non-retryable or needing user action.
39 Vitest files (32 unit, 7 integration) define 169 test cases, and the integration tests run against a real PostgreSQL instance through Testcontainers. GitHub Actions runs formatting, lint, typecheck, tests, the build, migration verification, npm audit and a multi-stage Docker build that runs as a non-root user.
Have a project in mind or a question about my work? Email or LinkedIn is the best way to reach me.