A feature-complete, real-time chat application built with Django + Channels on the backend and React + Vite on the frontend. The UI and feature set are closely modeled on Telegram Desktop — private chats, groups, channels, calls, folders, and a full settings/security suite — all backed by real, working logic rather than static mockups.
- Real-time messaging over WebSocket (Django Channels), with the notification socket auto-reconnecting (3s backoff) if the connection ever drops — a backend restart, laptop sleep, or network blip no longer silently kills live delivery until a manual page refresh
- Background/off-screen notifications are correctly scoped per conversation — leaving a chat (or closing the tab) clears the server-side "currently viewing" flag for it, so you keep getting real-time alerts for that chat instead of it going silent forever after the first visit
- Message status: sent / delivered / read
- Reply system, edit & delete messages, emoji reactions, pin/unpin
- File, image, and voice message sharing with chunked/multipart upload for large files
- Link preview cards
- Rich-text formatting (bold, italic,
inline code, auto-linked URLs) with a selection-triggered formatting toolbar - @mention autocomplete with highlighting and a notification that bypasses mute
- Per-conversation message drafts, restored automatically when you switch back
- Schedule a message to send later — pick a date & time; it stays hidden from everyone else in the chat until a Celery Beat task auto-publishes it at the right moment (with "Send now" / "Cancel" from a dedicated scheduled-messages panel)
- Per-message AI translate button + language preference toggle
- Full-text message search within a conversation (XSS-safe highlighting)
- Global search — find a message across every conversation from the sidebar search box, jump straight to it in context
- AI-powered chat search — ask a natural-language question and get an answer grounded in that conversation's history (DeepSeek)
- Polls — single- or multiple-choice, quiz mode with a post-vote correct-answer reveal, anonymous voting, participant-added options, revoting on/off, shuffled option order, and time-limited polls, all synced live over WebSocket
- Auto-delete messages after a configurable interval (Celery Beat periodic task)
- Export chat history as a downloadable, self-contained
.zip(HTML transcript + real photos/videos/files/voice folders) or plain.txt, with content-type, size-limit, and date-range filters - Desktop, sound & Web Push notifications (delivered via a Service Worker even when the tab/app is closed), typing indicator, online/offline presence
- View-once photos & videos — the recipient can open it exactly once; the file is deleted from storage the instant it's viewed, so it can never be re-fetched even via a leaked URL, and the sender can't reopen it either
- Per-conversation, per-user chat wallpaper — named gradient presets or your own uploaded photo, set from the "⋮" menu on any chat
- Stickers & GIF — a quick-send sticker panel plus live GIF search (Giphy), sent instantly with no bubble around the sticker
- Group creation as a two-step Telegram-style flow (name/photo, then a searchable member picker) with a real member-management panel afterward (mute, add members, manage, leave — admin-gated)
- Channel creation, editing (name/avatar/description/public-private/invite link) restricted to admins/owner
- Public channel discovery — search and join open channels
- Custom folders — create, delete, and assign chats/channels to a folder
- Chat header quick-actions menu — mute/unmute, view group/channel info, manage (admin-gated), create a poll, export chat history, report the chat to moderators, clear your own local history, leave
- Per-message reporting to moderators, plus a server-side banned-word filter that rejects a message before it's ever saved
- In-app moderation panel (staff-only) — review and resolve message/conversation reports, delete a reported message, manage the banned-word list, all without touching Django admin
- 1:1 audio & video calls over WebRTC
- coturn TURN relay server for reliable connectivity behind strict NAT/corporate networks (STUN alone isn't enough)
- Global call history across all conversations, grouped by contact, with one-tap redial
- "New Call" screen to dial any contact directly
- JWT authentication (SimpleJWT) + Google OAuth 2.0
- Two-Step Verification (2FA) — password, hint, and recovery email, matching Telegram's exact flow
- Active Sessions — real per-device session list with instant, server-side token revocation (REST and WebSocket); re-logging in from the same device reuses its existing row instead of piling up duplicates
- Local Passcode — client-side 4-digit PIN app lock (SHA-256, never leaves the device)
- Privacy controls — Everybody/Nobody visibility for last seen, profile photo, bio, phone number, and birthday, plus real enforcement (not just display) on who can message you, call you, send you voice messages, add you to groups, or see their name attached to messages you forward
- Profile: separate display name — a dedicated "Name" field independent of the
@usernamehandle, shown everywhere a person's identity is displayed app-wide (message bubbles, chat list, group/channel member lists, calls, search); falls back to the username when unset, and never affects @mentions or search-by-handle - My Account — Telegram-style Info screen: avatar with inline camera picker, auto-saving Bio (70-char limit), Name/Phone number/Username as tap-to-edit popup fields, a real Personal Channel picker (choose from channels you own), a per-user Name Color (persisted, shown as an accent pill), a Birthday picker, and a Chat Automation settings screen (bot handle + chat-access rules, saved locally — no bot platform integration yet)
- Real user blocking, enforced server-side on both conversation creation and message send
- Antivirus scanning via ClamAV for images and other unknown/small files (fails closed if the scanner is unreachable); videos and
.ziparchives skip the scan by design, and dangerous executable extensions (.exe,.bat,.sh, ...) are rejected outright before any bytes are uploaded - Rate limiting (DRF throttling) on login, 2FA verification, messaging, and uploads
DEBUGandALLOWED_HOSTSare environment-driven, not hardcoded, so a misconfigured deploy can't accidentally ship debug mode- Server-enforced total upload size cap (
MAX_UPLOAD_SIZE_MB), independent of any client-side check - Postgres, Redis, MinIO console, and ClamAV ports are bound to
127.0.0.1in Docker Compose — never exposed on the host network - Optional Sentry error monitoring on both backend and frontend (silently disabled when no DSN is configured)
- Conversation summarization
- Smart reply suggestions
- Per-message translation
- Grammar correction
- Installable on desktop and mobile (manifest + Service Worker) — runs like a native app, outside the browser chrome
- Background Web Push delivery survives a closed tab; the Service Worker suppresses duplicate notifications while the chat is already focused
- Four themes — Classic, Day, Tinted, Night — each with a genuinely distinct color palette
- Custom accent color picker
- Auto-night mode (follows the OS light/dark preference)
- Real-time UI language switching (English / Russian), applied instantly across the whole app without a reload
- Django 5 + Django REST Framework
- Django Channels (ASGI, served via daphne)
- PostgreSQL
- Redis (Channels layer + Celery broker)
- Celery + Celery Beat (background & periodic tasks)
- React 18 + Vite
- Axios, native WebSocket API
- WebRTC (
RTCPeerConnection)
- Docker & Docker Compose
- MinIO — S3-compatible object storage (chunked multipart upload)
- ClamAV — antivirus scanning
- coturn — STUN/TURN server for WebRTC
- JWT (SimpleJWT) with custom session-aware revocation
- Google OAuth 2.0
- DeepSeek API (AI features, including AI-powered chat search)
- Web Push (
pywebpush, VAPID) delivered through a Service Worker - Sentry (optional, DSN-gated) for backend and frontend error monitoring
React (Vite) ──REST API / WebSocket──▶ Django + Channels (daphne)
│
┌───────────────────────────┼────────────────────────────┐
▼ ▼ ▼ ▼ ▼
PostgreSQL Redis MinIO ClamAV coturn
(data store) (Channels layer, (S3 file (virus (WebRTC
Celery broker) storage) scan) TURN relay)
- Django REST Framework handles all business-logic HTTP endpoints
- Django Channels manages WebSocket connections (chat, calls, notifications)
- Redis backs both the Channels layer and the Celery task queue
- Celery + Celery Beat run background jobs — virus scanning, auto-delete of expired messages, publishing scheduled ("send later") messages
- MinIO stores all uploaded files; ClamAV scans each one before it's served
- coturn provides a real relay path for calls when a direct/STUN connection isn't possible
chat-app/
│
├── backend/
│ ├── apps/
│ │ ├── users/ # accounts, JWT auth, 2FA, sessions, blocking
│ │ ├── conversations/ # private/group/channel chats, membership, folders
│ │ ├── messaging/ # messages, attachments, reactions, calls, WebSocket consumer
│ │ ├── notifications/ # push/desktop notification delivery
│ │ └── ai/ # DeepSeek-backed summarize/translate/suggest/grammar
│ ├── utils/ # MinIO, ClamAV, link preview helpers
│ ├── config/ # settings, asgi/wsgi, celery, root routing
│ ├── docker-compose.yml
│ └── manage.py
│
├── chat-frontend/
│ ├── src/
│ │ ├── components/ # one component per feature/screen (57+)
│ │ ├── context/ # ChatsContext, CallContext, OnlineUsersContext
│ │ ├── pages/ # Login, Register, Chat
│ │ ├── utils/ # i18n, theme, localPasscode, formatTime, ...
│ │ └── styles/ # global stylesheets
│ └── public/
│
├── screenshots/
└── README.md
This is the way the project is actually developed and run — a single command brings up every service (backend, frontend, PostgreSQL, Redis, MinIO, ClamAV, Celery, Celery Beat, and coturn).
git clone https://github.com/your-username/chat-app.git
cd chat-app/backend
# create backend/.env — see "Environment Variables" below
docker compose up --build| Service | URL |
|---|---|
| Frontend | http://localhost:5177 |
| Backend API | http://localhost:8004/api |
| WebSocket | ws://localhost:8004/ws/ |
| MinIO console | http://localhost:9005 |
Django's ASGI server (daphne) does not auto-reload — restart the
backendcontainer after backend code changes:docker compose restart backend.
Backend
cd backend
python -m venv venv
venv\Scripts\activate # Windows
# source venv/bin/activate # Linux/Mac
pip install -r requirements.txt
python manage.py migrate
python -m daphne -b 127.0.0.1 -p 8000 config.asgi:applicationYou'll also need PostgreSQL, Redis, MinIO, and (optionally) a coturn instance running and reachable at the addresses configured in .env.
Frontend
cd chat-frontend
npm install
npm run devbackend/.env
SECRET_KEY=your-secret-key
DEBUG=True
DB_NAME=chatdb
DB_USER=postgres
DB_PASSWORD=your_password
DB_HOST=db
DB_PORT=5432
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_SECRET_KEY=your_google_client_secret
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_BUCKET_NAME=chatapp
MINIO_ENDPOINT=http://minio:9000
MINIO_REGION=us-east-1
DEEPSEEK_API_KEY=your_deepseek_api_key
# GIF search (free tier at developers.giphy.com) — GIF search returns an
# error without this, everything else in the app works fine
GIPHY_API_KEY=your_giphy_api_key
# coturn (WebRTC TURN relay)
TURN_REALM=chatapp.local
TURN_USERNAME=chatuser
TURN_PASSWORD=your_turn_password
TURN_EXTERNAL_IP=127.0.0.1
# admin account, created automatically on first boot
DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_EMAIL=admin@example.com
DJANGO_SUPERUSER_PASSWORD=a-strong-passwordchat-frontend/.env
VITE_API_URL=http://localhost:8004/api
VITE_WS_URL=ws://localhost:8004
VITE_GOOGLE_CLIENT_ID=your_google_client_id
VITE_TURN_URL=turn:localhost:3478
VITE_TURN_USERNAME=chatuser
VITE_TURN_PASSWORD=your_turn_password
.envfiles are gitignored on both sides — never commit real secrets. Vite only reads.envat server boot, so restart thefrontendcontainer after changing it.
- Email & password — JWT-based login (SimpleJWT)
- Google OAuth 2.0 — one-click sign-in (requires your own
GOOGLE_CLIENT_ID) - Two-Step Verification — an optional second factor (password + hint + recovery email) enforced at login before a token is issued
- Every uploaded file is scanned by a real ClamAV instance before it's servable; if the scanner itself is unreachable, the file is treated as unsafe rather than silently allowed through
- Passwords and the 2FA secret are hashed with Django's
make_password/check_password— never stored in plain text - JWT sessions are individually revocable — terminating a session in Active Sessions invalidates that token immediately for both REST requests and open WebSocket connections
- Message search results are HTML-escaped before rendering, closing off stored-content XSS via the search UI
- Rate limiting (DRF throttling) on login, 2FA verification, messaging, and file uploads
- CORS restricted to an explicit allow-list, never a wildcard
- All ORM-only data access — no raw SQL, no SQL-injection surface
- Secrets (
SECRET_KEY, DB/MinIO/DeepSeek credentials) are read exclusively from environment variables and are never committed to the repository
- Production-shaped, service-oriented architecture (Docker Compose across 9 services)
- Real-time messaging and calls with a genuine NAT-traversal fallback (TURN), not STUN-only
- Secure file pipeline: chunked upload → MinIO storage → ClamAV scan
- A settings and security suite (2FA, sessions, blocking, local passcode) built to match Telegram's actual behavior, not a placeholder UI
- Instant, real UI language switching with no page reload
- 🎙️ Voice-message-to-text transcription (blocked on a paid STT API key — DeepSeek doesn't support audio)
- 👥 Group video calls (currently 1:1 only — needs a mesh/SFU architecture)
- 📱 Native mobile app (React Native)
- 🌍 Additional UI languages beyond English/Russian
Telegram Premium/Stories-tier features (Live streams, Boosts, Story Archive, Send a Gift, custom fonts) are intentionally out of scope for a self-hosted chat app.
Akmal Axrorov
If you like this project, give it a ⭐ on GitHub!




