Loading

Automation · 2026

Telegram Translation Relay Bot

A Telegram bot that relays text between two people and translates each message into the language the recipient chose.

Year

2026

Type

Automation

Role

Solo Backend Developer

Tech Stack

  • Node.js
  • TypeScript
  • Fastify
  • Telegram Bot API
  • grammY
  • Prisma
  • PostgreSQL
  • Azure Translator
  • Docker
  • GitHub Actions
PrivatePrivate repository. Source code is not public.

Project Summary

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.

Problem & Solution

Problem
  • Copying messages into a separate translator breaks the conversation
  • Telegram bots cannot read private chats between two users
  • Webhook retries and translation API outages can lose or repeat messages
  • Machine translation can mangle links, mentions, code and emoji
  • A free translation tier and personal data both need hard limits on volume and retention
Solution
  • Each person chats with the bot, and it relays translated text to their paired contact
  • Single-use invite links that expire after 24 hours by default, plus approval from both people
  • A transactional outbox with idempotent update ingestion, leased jobs and capped retries
  • Protected-content placeholders that are restored and checked after translation
  • Per-user rate and backlog limits, a 64,000-character daily budget and 30-day message retention by default

Key Features

01

Invite and Consent

/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.

02

16-Language Translation

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.

03

Bilingual Bot Menu

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.

04

Safety Controls

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.

05

Reliable Delivery

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.

06

Owner Dashboard

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.

Technical Details

Backend

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.

Database

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.

Reliability

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.

Testing & CI

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? Let's talk.

Have a project in mind or a question about my work? Email or LinkedIn is the best way to reach me.