From 2f3ae25e90413a66847da36254e37b8381ba342b Mon Sep 17 00:00:00 2001 From: onysd Date: Mon, 13 Jul 2026 03:45:56 +0300 Subject: [PATCH] changed readme --- README.md | 226 ++++++++++++++---------------- media/readme/owpengram_splash.png | Bin 0 -> 10709 bytes 2 files changed, 104 insertions(+), 122 deletions(-) create mode 100644 media/readme/owpengram_splash.png diff --git a/README.md b/README.md index f52704fe..bf86897e 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,45 @@ -# gramsrv - Open Source Telegram Server / MTProto Server in Go +

+ OwpenGram +

-`gramsrv` is an open-source Telegram server implementation and MTProto server -written in Go. It is a Telegram-like backend for real client compatibility, -self-hosted chat experiments, protocol research, and long-running work toward a -practical community server. +# πŸš€ OwpenGram Server -If you are looking for a **Telegram server**, **MTProto server**, -**Telegram backend**, **Telegram clone server**, or **self-hosted -Telegram-like chat server**, this repository is the server-side implementation -to study, run, and improve. +**Your own private messaging server β€” self-hosted, protocol-compatible, fully yours.** -[Website](https://telesrv.net) Β· [Discussion group](https://t.me/telesrv_chat) Β· [Channel](https://t.me/telesrv) Β· [δΈ­ζ–‡ README](README.zh-CN.md) +OwpenGram Server is an open-source, Telegram-compatible MTProto backend written +in Go. Run it on your own network for a private, closed setup, or on a VPS to +be reachable anywhere in the world. Your data, your keys, your rules β€” no +cloud, no lock-in, no censorship. + +> πŸ”— Implements **MTProto API layer 227**. `gramsrv` is independent and unofficial. It is not affiliated with, endorsed by, or sponsored by Telegram or the official Telegram team. -## Project Keywords +--- -`telegram server` Β· `telegram server implementation` Β· `mtproto server` Β· -`mtproto server in go` Β· `telegram backend` Β· `telegram-like server` Β· -`self-hosted telegram` Β· `telegram desktop compatible server` Β· -`android telegram compatible server` Β· `open source chat server` +## ✨ Why OwpenGram? -## Demo Video +- πŸ”’ **Private & self-hosted** β€” messages live on infrastructure you control. +- 🧩 **Telegram-compatible** β€” works with the OwpenGram Android and Desktop clients. +- 🌍 **Reachable anywhere** β€” host it globally, or keep it on your own network. +- πŸ›‘οΈ **Censorship-resistant** β€” no central authority can shut you down. +- βš™οΈ **Single binary** β€” one Go program prepares keys, runs migrations, serves + MTProto, and dispatches updates and background workers. +- πŸ†“ **Free & open source** β€” Apache-2.0, audit and extend it freely. -https://github.com/user-attachments/assets/25e651dc-a022-4d60-8b9b-ca3e8bfe216c +## 🎯 What works today -## Project Traits +- πŸ’¬ Private chats, groups, supergroups & channels +- πŸ“ž Voice & group calls, live streams, SFU/TURN building blocks +- πŸ–ΌοΈ Media & files β€” photos, videos, documents, stickers, reactions +- πŸ€– Bots and mini apps, with a minimal Bot API gateway +- 🌐 Message translation and AI-assisted compose +- πŸ“‡ Contacts, dialogs sync, chat folders, public link landing pages +- πŸ–₯️ Admin API and web UI for operations -| Status | Trait | What it means | -|---|---|---| -| βœ… | One program startup | One Go binary prepares RSA keys, runs migrations, seeds data, opens MTProto, serves RPC handlers, dispatches updates, and starts workers. | -| βœ… | Fully open server code | Protocol edge, domain services, storage, compatibility handlers, media, updates, admin surfaces, and experiments are all in this repository. | - -## Feature Checklist - -Everything below is an implemented server-side capability in the open-source -codebase. +
+πŸ“‹ Full feature checklist (click to expand) | Status | Feature | What works today | |---|---|---| @@ -59,24 +62,31 @@ codebase. | βœ… | Desktop, Android, iOS, and Web focus | Telegram Desktop is the primary target, with Android, iOS, and Web compatibility paths actively covered by the same server. | Some items are compatibility-first or experimental, but they are real open -server code, not hidden product-only features. The next step is making these -paths stronger together. +server code, not hidden product-only features. +
-## Quick Start +## ⚑ Quick Start Requirements: -- Go 1.25 or newer -- Docker Desktop or Docker Engine with Compose -- OpenSSL, if you want to build a matching Telegram Desktop client +- **Go 1.25+** +- **Docker** (or Docker Desktop), for PostgreSQL and Redis +- OpenSSL, if you want to build a matching Telegram Desktop-based client -Start PostgreSQL and Redis: +**1. Clone the repository** + +```bash +git clone https://github.com/owpengram/owpengram-server.git +cd owpengram-server +``` + +**2. Start the infrastructure** (PostgreSQL + Redis) ```powershell docker compose -f deploy/docker-compose.yml up -d ``` -Build and run the single server program: +**3. Build and run the server** Windows (PowerShell): @@ -92,16 +102,19 @@ go build -o bin/gramsrv ./cmd/telesrv ./bin/gramsrv ``` -On first start, `gramsrv` creates `data/server_rsa.pem`, applies database +On first start, the server creates `data/server_rsa.pem`, applies database migrations, seeds bundled language packs, prepares optional media resources, starts MTProto on `0.0.0.0:2398`, and brings up the update/media/background workers in the same process. -Useful local environment variables: +> **Default local login code:** `12345` β€” change it before any real use! + +### βš™οΈ Configuration See the complete [English configuration reference](docs/configuration.en.md) or -the [Chinese configuration reference](docs/configuration.zh-CN.md). `.env.example` -is a copyable development template, not an exhaustive parameter dictionary. +the [Chinese configuration reference](docs/configuration.zh-CN.md). +`.env.example` is a copyable development template, not an exhaustive parameter +dictionary. Most commonly used variables: | Variable | Default | Meaning | |---|---:|---| @@ -110,55 +123,37 @@ is a copyable development template, not an exhaustive parameter dictionary. | `TELESRV_DC` | `2` | self-hosted DC id | | `TELESRV_DEV_AUTH_CODE` | `12345` | fixed login code for local development | | `TELESRV_AUTH_CODE_MAX_ATTEMPTS` | `5` | wrong-code attempts before the code hash is deleted | -| `TELESRV_LOGIN_EMAIL_ENABLE` | `false` | send login codes to confirmed login email addresses through SMTP | -| `TELESRV_LOGIN_EMAIL_REQUIRE_SETUP` | `false` | force phone login/registration to set a login email first | -| `TELESRV_SMTP_HOST` | empty | SMTP host used when login email verification is enabled | -| `TELESRV_PUBLIC_BASE_URL` | `https://telesrv.net` | canonical external base URL for username, sticker, emoji, and chatlist links | -| `TELESRV_PUBLIC_APP_SCHEME` | `telesrv` | custom URL scheme opened by public landing pages | -| `TELESRV_PUBLIC_WEB_BASE_URL` | `https://web.telesrv.net` | Web client base URL shown on public landing pages | -| `TELESRV_PUBLIC_APP_NAME` | `telesrv` | display product name for public landing pages | | `TELESRV_POSTGRES_DSN` | local Compose DSN | PostgreSQL connection string | | `TELESRV_REDIS_ADDR` | `127.0.0.1:6399` | Redis address | -| `TELESRV_LANGPACK_SEED_DIR` | `data/langpack` | bundled language pack seed directory | | `TELESRV_BLOB_DIR` | `data/blobs` | local media blob directory | -| `TELESRV_STICKER_SEED_DIR` | `data/sticker-seed` | optional sticker/reaction seed directory | | `TELESRV_PUBLIC_LINK_WEB_ADDR` | empty | optional public link landing listener, for example `127.0.0.1:2401` | | `TELESRV_BOT_API_ADDR` | empty | optional HTTP Bot API gateway listen address, for example `127.0.0.1:8081` | -| `TELESRV_BOT_API_UPDATE_RETENTION` | `24h` | retention window for unconfirmed Bot API `getUpdates` queue entries | | `TELESRV_AI_ENABLED` | `true` | enable AI compose entry points | -| `TELESRV_AI_PROVIDERS` | `local` | ordered AI provider chain, such as `local` or `kimi,local` | -| `TELESRV_AI_TIMEOUT` | `15s` | per AI provider call timeout | -| `TELESRV_AI_RATE_LIMIT` | `20` | per-account AI compose request budget | -| `TELESRV_AI_RATE_WINDOW` | `1m` | AI compose rate-limit window | -| `TELESRV_AI_LOG_CONTENT` | `false` | whether logs may include prompt/generated text | | `TELESRV_TRANSLATION_ENABLED` | `true` | enable Telegram message translation RPCs | -| `TELESRV_TRANSLATION_PROVIDERS` | empty | optional subset of configured remote AI providers for translation | -| `TELESRV_TRANSLATION_RATE_LIMIT` | `60` | per-account translated text item budget | -| `TELESRV_BUSINESS_AI_PROVIDER` | `echo` | Business automation reply provider | -The optional sticker seed directory is skipped when it does not exist. -Optional OpenAI-compatible, Kimi/Moonshot, Gemini, and Anthropic provider -variables are documented in `.env.example`. +Optional OpenAI-compatible, Kimi/Moonshot, Gemini, and Anthropic AI provider +variables, login email/SMTP settings, and Business AI settings are documented +in `.env.example` and the configuration reference. -## Public Deployment Ports +## πŸ”Œ Ports to open -When deploying `gramsrv` on a public server, open the following ports according -to the features you enable. +When deploying on a public server, open the following according to the +features you enable. -### Minimal public deployment (chat only) +**Minimal (chat only)** | Port | Protocol | Purpose | Required | |---|---|---|---| | 2398 | TCP | MTProto main port; also handles WebSocket when `TELESRV_WEBSOCKET_ENABLE=true` | Yes | -### With Admin backend +**With admin backend** | Port | Protocol | Purpose | Notes | |---|---|---|---| | 2399 | TCP | Admin REST API | Restrict to trusted IPs or put behind VPN | | 2600 | TCP | Admin Web UI | Use Nginx/reverse proxy + HTTPS in production | -### Optional feature ports +**Optional features** | Port | Protocol | Purpose | When needed | |---|---|---|---| @@ -169,7 +164,7 @@ to the features you enable. | configurable | TCP | Bot API | When `TELESRV_BOT_API_ADDR` is set | | 2401 example | TCP | Public username/sticker/chatlist landing pages | When `TELESRV_PUBLIC_LINK_WEB_ADDR=127.0.0.1:2401` is set | -### Internal/debug ports (do not expose publicly) +**Internal/debug (do not expose publicly)** | Port | Default bind | Purpose | |---|---|---| @@ -180,21 +175,13 @@ to the features you enable. Make sure `TELESRV_LISTEN=0.0.0.0:2398` is set, and `TELESRV_ADVERTISE_IP` points to your public IP so clients can connect. -## Public Link Landing Pages +## 🌐 Public link landing pages -`gramsrv` can serve public landing pages for `/`, profile avatars, +The server can serve public landing pages for `/`, profile avatars, `/addstickers/`, `/addemoji/`, and `/addlist/`. -Use `TELESRV_PUBLIC_LINK_WEB_ADDR` as the local HTTP bind address: - ```env TELESRV_PUBLIC_LINK_WEB_ADDR=127.0.0.1:2401 -``` - -Use `TELESRV_PUBLIC_BASE_URL` as the external canonical URL shown in generated -links: - -```env TELESRV_PUBLIC_BASE_URL=https://your-domain.example TELESRV_PUBLIC_APP_SCHEME=yourapp TELESRV_PUBLIC_WEB_BASE_URL=https://web.your-domain.example @@ -204,35 +191,39 @@ TELESRV_PUBLIC_APP_NAME=YourApp In production, keep `TELESRV_PUBLIC_LINK_WEB_ADDR` on loopback and reverse-proxy the public routes to it with HTTPS. -## Client Compatibility +## πŸ“± Connect a client -Stock Telegram clients will not connect to `gramsrv` because they trust -Telegram's production DC list and RSA keys. Use a patched experience client from -the [official website](https://telesrv.net), or build your own client with a -minimal protocol patch. +Use the OwpenGram clients, which have a built-in **Add Server** option on the +server-selection screen at login β€” no source patching or custom build needed: -Current Telegram Desktop baseline: +- πŸ€– [Android client](https://github.com/owpengram/owpengram-android-client) +- πŸ’» [Desktop client](https://github.com/owpengram/owpengram-desktop-client) -- Telegram Desktop commit: `9caf32dffc90ddd9bb08ad5777b865f729fa167b` -- TL layer: 227 -- Local DC: `127.0.0.1:2398`, DC id `2` +A stock Telegram client will not connect, since it only trusts Telegram's own +DC list and RSA keys. -After `gramsrv` generates `data/server_rsa.pem`, export the matching public key: +**1. Export your server's public key** + +After the server generates `data/server_rsa.pem`, export the matching public +key as PEM: ```powershell openssl rsa -in data/server_rsa.pem -RSAPublicKey_out -out data/server_rsa.pub ``` -Patch `Telegram/SourceFiles/mtproto/mtproto_dc_options.cpp`: +**2. Add the server in the client** -1. Replace the built-in production/test DC lists with your `gramsrv` endpoint. -2. Replace both `kPublicRSAKeys` and `kTestPublicRSAKeys` with - `data/server_rsa.pub`. -3. Add `Flag::f_tcpo_only` to the built-in DC flags. +On the login screen, open server selection β†’ **Add Server**, and fill in: -Keep the client patch minimal: endpoint, RSA key, and TCP-only flags only. +- **Host** β€” your server's address (e.g. `192.168.1.50` or `chat.example.com`) +- **Port** β€” `2398` by default +- **Main data center** β€” the DC id from `TELESRV_DC` (`2` by default) +- **RSA Public Key** β€” paste the full contents of `data/server_rsa.pub` + (the `-----BEGIN RSA PUBLIC KEY-----...` PEM block) into the key field -## Multi-Device Smoke Test +Current baseline: TL layer `227`. + +## πŸ§ͺ Development: multi-device smoke test Use separate client working directories so sessions do not share local `tdata`: @@ -242,10 +233,8 @@ Start-Process $tdesktop -ArgumentList @("-workdir", "$PWD\.tdata-alice") Start-Process $tdesktop -ArgumentList @("-workdir", "$PWD\.tdata-bob") ``` -Log in with different phone numbers. In local development, the login code is -`12345` unless you changed `TELESRV_DEV_AUTH_CODE`. - -Recommended checks: +Log in with different phone numbers β€” the local login code is `12345` unless +you changed `TELESRV_DEV_AUTH_CODE`. Recommended checks: - Send private messages, stickers, media, replies, forwards, edits, deletes, and read receipts between two users. @@ -256,11 +245,7 @@ Recommended checks: - Check server logs for no new `NOT_IMPLEMENTED`, `Unhandled RPC`, `bad_msg`, panic, or internal errors. -## Contributors - -- [ajarshia](https://github.com/ajarshia) - Android Persian (`fa`) language pack. - -## Repository Layout +## πŸ“‚ Repository layout ```text cmd/telesrv/ server entrypoint @@ -277,15 +262,9 @@ internal/sfu/ real-time SFU experiments internal/turnsrv/ TURN/STUN building blocks ``` -## TODO List +## 🀝 Contributing -- Improve Bot compatibility for third-party libraries, such as `python-telegram-bot`. -- Fix known bugs and keep hardening existing compatibility paths. - -## Help Improve It - -`gramsrv` will get better fastest if more people run it, break it, profile it, -and send focused improvements. Helpful contributions include: +This server gets better fastest with real usage and focused fixes: - Telegram Desktop and Android compatibility reports with reproducible steps. - RPC traces for startup, sync, chat, media, calls, bots, or edge cases. @@ -294,20 +273,23 @@ and send focused improvements. Helpful contributions include: and channel behavior. - Performance work on hot paths such as fan-out, pagination, storage queries, media upload/download, and connection handling. -- Setup improvements that make the one-program local experience smoother. If a change affects visible client behavior, please include the client -version/commit, the RPC path you tested, and whether server logs stayed free of -new `NOT_IMPLEMENTED`, `Unhandled RPC`, `bad_msg`, panic, or internal errors. +version/commit, the RPC path you tested, and whether server logs stayed free +of new `NOT_IMPLEMENTED`, `Unhandled RPC`, `bad_msg`, panic, or internal errors. -## License +## πŸ’¬ Community -`gramsrv` is released under the [Apache License 2.0](LICENSE). You may use, -modify, distribute, and use it commercially under the terms of Apache-2.0. +- πŸ“’ Channel: [@owpengram](https://t.me/owpengram) +- πŸ’¬ Chat: [Join the discussion](https://t.me/+sVB6Ymv70jEwNTAy) -## Custom Development +OwpenGram Server builds on the open-source +[gramsrv](https://github.com/iamxvbaba/gramsrv) project. -For paid custom development, you can contact the author through the discussion -group or website. Custom work can cover server features, Telegram Desktop, -Android, Web, deployment, compatibility adaptation, or other client/server -paths around this project. +## πŸ“„ License + +[Apache License 2.0](LICENSE) + +--- + +⭐ If OwpenGram is useful to you, a star on GitHub helps the project grow. diff --git a/media/readme/owpengram_splash.png b/media/readme/owpengram_splash.png new file mode 100644 index 0000000000000000000000000000000000000000..a7eab1e2f593b72f349f893786f172f96f2530a3 GIT binary patch literal 10709 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