diff --git a/cmd/createuser/main.go b/cmd/createuser/main.go deleted file mode 100644 index 579a4e0c..00000000 --- a/cmd/createuser/main.go +++ /dev/null @@ -1,153 +0,0 @@ -// Command createuser inserts a users row with an operator-chosen id, bypassing -// the normal users_id_seq auto-assignment. This works because users.id is -// GENERATED BY DEFAULT AS IDENTITY (not GENERATED ALWAYS) -- an explicit id in -// the INSERT is honored, the same mechanism ensureOfficialSystemUserWithDB -// (internal/store/postgres/message_send.go) already relies on to seed the -// built-in system accounts (ChatBot, BotFather, ...) at their fixed ids. -// -// Normal signup (auth.signUp) never lets a caller pick an id, so this exists -// purely for local/dev tooling -- reserving a specific low id (below -// OfficialSystemUserID=777000, say) for a test account. -// -// Usage: -// -// createuser -id 1000 [-first-name Test] [-last-name User] [-username testuser] -phone "15550001234" -// createuser -id 1000 [-first-name Test] [-last-name User] [-username testuser] -email "test@example.com" -// -// -phone and -email are mutually exclusive: an email-signup account never -// stores the address in users.phone directly (see internal/domain/emailphone.go) -// -- it gets a synthetic "888"-prefixed display phone instead (the same one -// assignEmailSignupDisplayPhone hands a real email-signup account), with the -// real address recorded separately in signup_email. -// -// Reads TELESRV_POSTGRES_DSN the same way the server does (internal/config). -package main - -import ( - "context" - "crypto/rand" - "encoding/binary" - "flag" - "fmt" - "os" - "time" - - "github.com/jackc/pgx/v5/pgxpool" - - "telesrv/internal/config" - "telesrv/internal/domain" -) - -// maxEmailSignupPhoneAttempts bounds the display-phone collision-retry loop, -// mirroring internal/app/auth/service.go's own constant of the same name. -const maxEmailSignupPhoneAttempts = 20 - -func randomInt64() (int64, error) { - var b [8]byte - if _, err := rand.Read(b[:]); err != nil { - return 0, fmt.Errorf("rand: %w", err) - } - return int64(binary.LittleEndian.Uint64(b[:])), nil -} - -func main() { - id := flag.Int64("id", 0, "user id to create (required)") - firstName := flag.String("first-name", "Test", "first name") - lastName := flag.String("last-name", "", "last name") - username := flag.String("username", "", "username, without @ (optional)") - phone := flag.String("phone", "", "phone number (optional; mutually exclusive with -email)") - email := flag.String("email", "", "email address for an email-signup account (optional; mutually exclusive with -phone)") - force := flag.Bool("force", false, "skip the reserved-id / sequence-collision safety checks") - flag.Parse() - - if *id <= 0 { - fmt.Fprintln(os.Stderr, "createuser: -id is required and must be positive") - os.Exit(2) - } - if *phone != "" && *email != "" { - fmt.Fprintln(os.Stderr, "createuser: -phone and -email are mutually exclusive") - os.Exit(2) - } - if !*force { - if domain.IsSystemUserID(*id) { - fmt.Fprintf(os.Stderr, "createuser: %d is a reserved built-in system account id (see internal/domain/system.go) - refusing, pass -force to override\n", *id) - os.Exit(2) - } - if *id >= domain.UserIDSequenceBase { - fmt.Fprintf(os.Stderr, "createuser: %d is >= UserIDSequenceBase (%d) - a future organic signup could eventually collide with it; pass -force to proceed anyway (then consider bumping users_id_seq yourself)\n", *id, domain.UserIDSequenceBase) - os.Exit(2) - } - } - - cfg, err := config.Load() - if err != nil { - fmt.Fprintf(os.Stderr, "createuser: load config: %v\n", err) - os.Exit(1) - } - - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - - pool, err := pgxpool.New(ctx, cfg.PostgresDSN) - if err != nil { - fmt.Fprintf(os.Stderr, "createuser: connect: %v\n", err) - os.Exit(1) - } - defer pool.Close() - - accessHash, err := randomInt64() - if err != nil { - fmt.Fprintf(os.Stderr, "createuser: %v\n", err) - os.Exit(1) - } - - displayPhone := *phone - signupEmail := "" - if *email != "" { - signupEmail = domain.NormalizeEmailForPhone(*email) - displayPhone, err = assignEmailSignupDisplayPhone(ctx, pool) - if err != nil { - fmt.Fprintf(os.Stderr, "createuser: %v\n", err) - os.Exit(1) - } - } - - // phone/username/signup_email all sit under partial unique indexes that - // exclude '', so leaving any of them blank never collides with another - // blank-valued account. - row := pool.QueryRow(ctx, ` - INSERT INTO users (id, access_hash, phone, signup_email, first_name, last_name, username, country_code) - VALUES ($1, $2, $3, $4, $5, $6, $7, '') - ON CONFLICT (id) DO NOTHING - RETURNING id`, - *id, accessHash, displayPhone, signupEmail, *firstName, *lastName, *username) - - var createdID int64 - if err := row.Scan(&createdID); err != nil { - fmt.Fprintf(os.Stderr, "createuser: id %d already exists (or insert failed): %v\n", *id, err) - os.Exit(1) - } - - fmt.Printf("created user id=%d access_hash=%d first_name=%q last_name=%q username=%q phone=%q signup_email=%q\n", - createdID, accessHash, *firstName, *lastName, *username, displayPhone, signupEmail) -} - -// assignEmailSignupDisplayPhone mirrors internal/app/auth/service.go's method -// of the same name: pick a random "888"-prefixed display phone and re-roll on -// the astronomically unlikely collision with an existing account's phone. -func assignEmailSignupDisplayPhone(ctx context.Context, pool *pgxpool.Pool) (string, error) { - for range maxEmailSignupPhoneAttempts { - candidate, err := domain.NewEmailSignupDisplayPhone(domain.EmailPhonePrefix) - if err != nil { - return "", err - } - var exists bool - if err := pool.QueryRow(ctx, `SELECT EXISTS (SELECT 1 FROM users WHERE phone = $1)`, candidate).Scan(&exists); err != nil { - return "", fmt.Errorf("check display phone collision: %w", err) - } - if !exists { - return candidate, nil - } - } - return "", fmt.Errorf("assign email signup display phone: exhausted %d attempts", maxEmailSignupPhoneAttempts) -} diff --git a/docs/telegram-feature-comparison.md b/docs/telegram-feature-comparison.md deleted file mode 100644 index 174fb19a..00000000 --- a/docs/telegram-feature-comparison.md +++ /dev/null @@ -1,286 +0,0 @@ -# OwpenGram Server vs. Telegram - Feature Comparison - -This document compares what OwpenGram Server actually implements against the -behaviour of Telegram's official server, as seen through the MTProto API -(layer 228) and the official clients (Telegram Desktop is the primary target, -with Android / iOS / Web compatibility paths). - -It is written from the server code in this repository: ~633 canonical -`registerRPC` handlers across `internal/rpc`, plus the domain services in -`internal/app/*`. "Telegram" below means the closed official backend. - -Legend: - -- **Full** - implemented with real server-side state and semantics -- **Partial** - core paths work; edges, scale features, or moderation depth missing -- **Stub** - RPC is answered with a fixed/empty valid response so clients don't - hang or crash, but there is no feature behind it -- **None** - not registered / not implemented - ---- - -## 1. Transport, auth keys, sessions - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| MTProto 2.0 over TCP | Full | TCP transport, RSA key exchange, auth keys, salts, ack/resend, bad-msg notifications, RPC dispatch | Full | -| Obfuscated / padded intermediate / other transports | Full (TCP, HTTP, WS, obfuscation) | intermediate / padded-intermediate focus | Partial | -| Multiple data centers, CDN DCs, DC migration | Full (5+ DCs, `PHONE_MIGRATE`, CDN file DCs) | Single logical DC; `help.getConfig` advertises one; no CDN redirects | None (by design) | -| Perfect-forward-secrecy temp auth keys (`auth.bindTempAuthKey`) | Full | Implemented | Full | -| Takeout / data export sessions | Full (`account.initTakeoutSession`) | Not registered | None | -| Web / bot authorizations listing & reset | Full | `account.getWebAuthorizations`, `resetWebAuthorization(s)` | Full | - -## 2. Login and accounts - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Phone-number login with SMS/flash-call/app code | Full, global SMS delivery | Dev login code; external delivery via SMS webhook or SMTP; email as identity (no phone required) | Partial (delivery is operator-provided) | -| QR-code login (`auth.exportLoginToken` / `acceptLoginToken`) | Full | Implemented | Full | -| Cloud password / 2FA (SRP) | Full | `account.getPassword`, `updatePasswordSettings`, `auth.checkPassword`, recovery e-mail, `resetPassword` | Full | -| Passkey / WebAuthn sign-in | Not in official server | `auth.initPasskeyLogin` / `finishPasskeyLogin`, `account.*Passkey*` | Extra (OwpenGram-only) | -| "Login with Telegram" as an OIDC provider for 3rd-party sites | Not applicable | Self-hosted OpenID Connect provider (`internal/telegramloginhttp`) | Extra | -| Sign-up, terms of service, delete account, account TTL | Full | Implemented incl. `account.setAccountTTL`, `deleteAccount` with reason | Full | -| Login email as second factor | Full | Implemented | Full | -| Active sessions / authorizations management | Full | `account.getAuthorizations`, `resetAuthorization`, TTL, `changeAuthorizationSettings` | Full | - -## 3. Users, contacts, privacy - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Profiles, bios, profile photos, personal channel | Full | `users.getFullUser`, `photos.*`, `account.updatePersonalChannel` | Full | -| Usernames + collectible/fragment usernames | Full | Mint / transfer / activate, `account.reorderUsernames`, `fragment.getCollectibleInfo` | Full | -| Contact import / export / search / resolve phone | Full | `contacts.importContacts`, `resolvePhone`, `search`, close friends | Full | -| Blocked list, privacy rules, "who can..." keys | Full (all privacy keys) | `account.getPrivacy` / `setPrivacy`, global privacy settings, blocked list | Partial (common keys; some newer keys may be defaulted) | -| Presence / last seen | Full | `account.updateStatus`, `contacts.getStatuses`, presence fan-out | Full | -| Birthdays, close friends, contact notes | Full | `contacts.getBirthdays`, `editCloseFriends`, `updateContactNote` | Full | -| Global name search directory | Full | `contacts.search` over local users/chats | Partial (local instance only) | - -## 4. Messaging (private chats) - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Send / edit / delete / forward / reply | Full | `messages.sendMessage` / `sendMedia` / `sendMultiMedia` / `editMessage` / `forwardMessages` | Full | -| Rich entities, formatted text, link previews | Full | Entities, `messages.getWebPage` / `getWebPagePreview`, TDesktop rich messages | Full | -| Albums / grouped media | Full | `album_group` grouping | Full | -| Reactions (emoji + custom emoji), paid reactions | Full incl. paid (Stars) | `sendReaction`, available/recent/top/default reactions, tags; **no paid reactions** | Partial | -| Scheduled messages | Full | `getScheduledHistory`, `sendScheduledMessages`, `deleteScheduledMessages` | Full | -| Self-destruct / TTL, default history TTL | Full | `setHistoryTTL`, `setDefaultHistoryTTL`, TTL-oriented paths | Partial | -| Read receipts, read date, "who read" in groups | Full | `readHistory`, `getOutboxReadDate`, `getMessageReadParticipants` | Full | -| Drafts (incl. cloud drafts sync) | Full | `saveDraft`, `getAllDrafts`, `clearAllDrafts` | Full | -| Saved Messages, saved dialogs, pinned saved | Full | `getSavedDialogs`, `getSavedHistory`, `toggleSavedDialogPin` | Full | -| Quick replies (business) | Full | `getQuickReplies`, `sendQuickReplyMessages`, `editQuickReplyShortcut` | Full | -| Translation (`messages.translateText`) | Full | Provider-backed batch translation, per-peer language, rate limits | Full | -| Transcribe voice (`messages.transcribeAudio`) | Full (Premium) | Registered | Partial (provider-dependent) | -| Fact-check / sponsored-message plumbing | Full | `getSponsoredMessages`, `viewSponsoredMessage`, `reportSponsoredMessage` | Partial (plumbing) | -| To-do lists in messages | Full | `appendTodoList`, `toggleTodoCompleted` | Full | -| Search: in-chat, global, by date, counters, calendar | Full | `messages.search`, `searchGlobal`, `getSearchCounters`, `getSearchResultsCalendar` / `Positions` | Full | - -## 5. Groups, supergroups, channels - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Basic groups create / add / migrate to supergroup | Full | `messages.createChat`, `addChatUser`, `migrateChat` | Full | -| Supergroups / channels create, join, leave, delete | Full | `channels.createChannel` / `joinChannel` / `leaveChannel` / `deleteChannel` | Full | -| Admin rights, banned rights, default banned rights, ranks | Full | `channels.editAdmin` / `editBanned`, `editChatDefaultBannedRights`, `editChatParticipantRank` | Full | -| Invite links (permanent, named, request-to-join, importers) | Full | `messages.exportChatInvite`, `getExportedChatInvites`, `hideChatJoinRequest`, importers | Full | -| Participants list, admin log, hidden participants | Full | `channels.getParticipants`, `getAdminLog`, `toggleParticipantsHidden` | Full | -| Forum topics | Full | `getForumTopics`, `editForumTopic`, pinned topics, view-as-messages | Full | -| Linked discussion group | Full | `setDiscussionGroup`, `getGroupsForDiscussion`, `readDiscussion` | Full | -| Slow mode, join-to-send, join-request, anti-spam, gigagroup | Full | `toggleSlowMode`, `toggleJoinToSend`, `toggleAntiSpam`, `convertToGigagroup` | Full | -| Public username directory + previews for non-members | Full | `channels.searchPosts`, resolve, public landing pages | Full | -| Boosts / boost level perks | Full | `premium.applyBoost`, `getBoostsStatus`, `getMyBoosts` | Partial (levels tracked; not all perks gated) | -| Channel monetization, paid posts, suggested posts | Full (Stars/TON) | `service_suggested_post`, `toggleSuggestedPostApproval`, `updatePaidMessagesPrice` | Partial (no real payout ledger) | -| Statistics (channel / megagroup / message / story) | Full | `stats.getBroadcastStats`, `getMegagroupStats`, `getMessageStats`, `loadAsyncGraph` | Partial (graphs from local data) | -| Communities / peer links (layer 228) | Full | `communities.*` create / join / peer links / bans | Partial (new API) | - -## 6. Media and files - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Upload / download, big-file parts, file hashes | Full | `upload.saveFilePart` / `saveBigFilePart` / `getFile` / `getFileHashes` | Full | -| CDN-backed downloads, `upload.getCdnFile` | Full | None - always served from origin | None (by design) | -| Storage backends | Google infra | Local disk **or** S3/MinIO-compatible, switchable per deployment; low-space guard; stale-media cleanup | Full (self-host) | -| Web files / proxied external media (`upload.getWebFile`) | Full | Registered, external media fetch | Partial | -| Photos, documents, thumbnails, GIFv conversion, video | Full | Implemented incl. canonical GIFv conversion | Full | -| Web page previews, instant view | Full | Previews yes; **Instant View pages** no | Partial | -| Map/venue tile cache | Full | Cache hooks only | Partial | - -## 7. Stickers, emoji, GIFs - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Sticker sets install/archive/reorder, custom emoji, masks | Full | `messages.*StickerSet*`, `getCustomEmojiDocuments`, mask stickers | Full | -| Create / edit own sticker set (`stickers.*`) | Full | `createStickerSet`, `addStickerToSet`, `renameStickerSet`, suggest short name | Full | -| Featured / trending / recent / faved stickers | Full | `getFeaturedStickers`, `getRecentStickers`, `getFavedStickers` | Full | -| Emoji keywords / groups / status | Full | `getEmojiKeywords*`, `getEmojiGroups`, emoji status incl. collectible | Full | -| Saved GIFs + inline `@gif` catalog | Full | Admin-curated categorized `@gif` catalog, auto-save on send | Full (plus extras) | -| Premium animated emoji / effects | Full | `messages.getAvailableEffects` | Partial | - -## 8. Bots and mini apps - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Bot messaging, callbacks, inline mode | Full | `getInlineBotResults`, `sendInlineBotResult`, `getBotCallbackAnswer`, `setBotCallbackAnswer` | Full | -| BotFather-style bot creation & config | Full | `bots.createBot`, `setBotInfo`, `setBotCommands`, menu button, usernames | Full | -| Web apps / mini apps (`messages.requestWebView`, main/app/simple) | Full | `webViewRequest`, `mainWebViewRequest`, `appWebViewRequest`, `prolongWebView`, `sendWebViewData` | Full | -| Attachment-menu bots | Full | `getAttachMenuBots`, `toggleBotInAttachMenu` | Full | -| Bot API HTTP gateway (getUpdates / webhooks) | Full (`api.telegram.org`) | Minimal Bot API gateway in `internal/botapi`; persistent `getUpdates`, webhook delivery | Partial (subset of methods) | -| Business connections / Business AI replies | Full | `account.getBotBusinessConnection`, connected bots, business automation, AI echo | Partial | -| Bot payments (`payments.sendPaymentForm`, invoices) | Full | **Not registered** | None | -| Star-ref / affiliate programs | Full | `bots.updateStarRefProgram` shell | Stub | -| Games (`messages.setGameScore`, high scores) | Full | Registered incl. inline high scores | Full | - -## 9. Calls and live streams - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| 1:1 call signaling (DH, `phone.requestCall` ... `discardCall`) | Full | Full state machine with g_a hash commit, ring timeout, tombstones (`internal/app/phone`) | Full (signaling) | -| Group calls / voice chats | Full | `phone.joinGroupCall`, participants, `editGroupCallParticipant`, titles, scheduled starts | Full (signaling/state) | -| Conference calls (layer 228 chain blocks) | Full | `createConferenceCall`, `getGroupCallChainBlocks`, invite/decline | Partial | -| Media relay | Global TURN + SFU fleet | SFU/TURN **building blocks** (`internal/sfu`, `internal/turnsrv`); operator must run relays | Partial | -| RTMP live streaming into channels | Full | `phone.getGroupCallStreamRtmpUrl`, RTMP ingest, segmenter (`internal/app/livestream`) | Full | -| Screen sharing / presentation | Full | `joinGroupCallPresentation` / `leaveGroupCallPresentation` | Full (signaling) | -| Call debug / rating | Full | `saveCallDebug`, `setCallRating` | Full | - -## 10. Stories - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Post / edit / delete stories, media | Full | `stories.sendStory`, `editStory`, `deleteStories`, `canSendStory` | Full | -| Read state, views, viewers list, reactions | Full | `readStories`, `incrementStoryViews`, `getStoryViewsList`, `sendReaction` | Full | -| Pinned stories / profile grid, archive | Full | `togglePinned`, `getPinnedStories`, `getStoriesArchive` | Full | -| Story albums (layer 228) | Full | `createAlbum`, `getAlbumStories`, `reorderAlbums` | Full | -| Stealth mode, hidden peers | Full | `activateStealthMode`, `toggleAllStoriesHidden`, `togglePeerStoriesHidden` | Full | -| Channel stories + live stories | Full | Channel posting via access checks; `stories.startLive` | Partial | -| Story boosts / repost / public forwards | Full | `stats.getStoryPublicForwards`, search posts | Partial | - -## 11. Dialogs, sync, folders - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Dialog list, pinned, manual unread, archive folder | Full | `getDialogs`, `getPinnedDialogs`, `markDialogUnread`, `folders.editPeerFolders` | Full | -| Chat folders / dialog filters, suggested filters | Full | `getDialogFilters`, `updateDialogFilter`, `getSuggestedDialogFilters`, filter tags | Full | -| Shareable folders / chatlist invites | Full | `chatlists.exportChatlistInvite`, join/import, updates, revoked handling | Full | -| Update sequencing: `updates.getState` / `getDifference` / `getChannelDifference` | Full | Implemented incl. durable updates, `pts`/`qts`/`seq`, offline difference recovery | Full | -| Real-time push over the MTProto connection | Full | Online fan-out, reliable dispatch | Full | -| **External push (APNs / FCM / GCM)** | Full | **None** - `account.registerDevice` only records the in-connection MTProto push session; other token types are ignored | None (by design) | -| Notification settings / exceptions / reactions-notify | Full | `getNotifySettings`, `getNotifyExceptions`, `getReactionsNotifySettings` | Full | - -## 12. Secret chats (E2E) - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| DH handshake, `messages.requestEncryption` -> accept/discard | Full | State machine + id/access_hash allocation, blind g_a storage (`internal/app/secretchat`) | Full | -| Encrypted message / file / service delivery, `qts` queue | Full | `sendEncrypted`, `sendEncryptedFile`, `sendEncryptedService`, `uploadEncryptedFile` | Full | -| Encrypted typing, read history, spam report | Full | `setEncryptedTyping`, `readEncryptedHistory`, `reportEncryptedSpam` | Full | -| Rekeying / perfect forward secrecy for long chats | Full | Handled at handshake layer; server is a blind relay | Partial | - -## 13. Themes, wallpapers, appearance - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Wallpapers install/upload/reset, multi-wallpaper | Full | `account.getWallPapers`, `installWallPaper`, `getMultiWallPapers` | Full | -| Custom themes create/install/update, chat themes | Full | `account.createTheme`, `installTheme`, `getChatThemes`, `messages.setChatTheme` | Full | -| Peer colors, profile colors, name colors | Full | `help.getPeerColors`, `account.updateColor`, `channels.updateColor` | Full | -| Unique-gift chat themes | Full | `account.getUniqueGiftChatThemes` (empty - no gifts) | Stub | -| Ringtones | Full | `account.getSavedRingtones` | Full | - -## 14. Payments, Stars, gifts, Premium - deliberately omitted - -Telegram's economy layer is intentionally **not implemented**. OwpenGram keeps -a handful of read-only RPCs answered with valid empty/zero responses purely so -the official clients render "no Stars / no gifts" instead of hanging or -retrying in a storm. - -| Area | Telegram | OwpenGram | -|---|---|---| -| Invoice / payment form / shipping / checkout | Full (`payments.sendPaymentForm`, ...) | Not registered | -| Telegram Stars balance, purchase, transactions | Full | `getStarsStatus` / `Subscriptions` / `Transactions` return zero balance, empty ledger | -| Star gifts / collectible gifts / gift profiles | Full | `getStarGifts` / `getSavedStarGifts` return empty lists | -| Google Play / App Store receipt verification | Full | `canPurchaseStore` returns `false`; `assignPlayMarketTransaction` -> `STORE_PAYMENT_UNAVAILABLE` | -| Premium subscription, gift codes, giveaways | Full | `payments.getPremiumGiftCodeOptions` empty; Premium status can be granted server-side by the operator, not purchased | -| TON / channel revenue withdrawal | Full | `getStarsRevenueStats` / `RevenueAdsAccountUrl` return fixed zero/compat values | -| Business Stars / paid messages payout | Full | Price fields tracked; no real ledger or payout | - -## 15. Moderation, admin, operations - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Report spam / peer / message / reaction / profile photo | Full | `messages.report`, `account.reportPeer`, `reportProfilePhoto`, `channels.reportSpam` | Full | -| Global ban / spam-bot / account restrictions | Full (internal) | Per-account freeze (admin read-only restriction advertised via appConfig), moderation cases, appeal links (`internal/app/moderation`) | Partial | -| Anti-spam service for groups | Full (`@GroupAnti-SpamBot`) | `channels.toggleAntiSpam`, `reportAntiSpamFalsePositive` | Partial | -| Admin API + web UI | Not public | RBAC-scoped admin API tokens, web UI (`internal/adminapi`, `internal/web`) | Extra | -| Broadcast / announcements to all users | Not public | Admin-panel broadcast from the official account to all or a picked list | Extra | -| Shared-device detection across accounts | Internal | Implemented | Extra | -| TUI server panel (setup wizard, start/stop, git-pull update, log tail, .env editor) | N/A | Bundled (`tui-panel`) | Extra | -| Metrics, pprof, DB tracing, load-test harness | Internal | `internal/observability`, `internal/loadtest`, `internal/loadharness` | Extra | - -## 16. Verification and badges - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| Official blue checkmark | Full (manual) | `@verifybot` flow, verification worker | Full (self-host policy) | -| Third-party bot verification mark (icon + label before a name) | Full (`bots.setCustomVerification`) | `@marksbot` mechanism - **experimental, hidden by default** | Partial | -| Scam / fake labels | Full | Flag paths present | Partial | - -## 17. Misc API surface - -| Area | Telegram | OwpenGram | Status | -|---|---|---|---| -| `help.getConfig` / `getAppConfig` / `getCountriesList` / timezones / promo | Full | Implemented | Full | -| Language packs (`langpack.*`) | Full | `getLangPack`, `getDifference`, `getStrings`, seeded packs | Full | -| App update check (`help.getAppUpdate`) | Full | Registered | Partial (operator-fed) | -| Deep-link info, t.me link resolution | Full | `help.getDeepLinkInfo`, public link landing pages | Full | -| `contacts.getLocated` (nearby people/chats) | Full | Not registered | None | -| Peer color / emoji-status catalogs | Full | Implemented | Full | -| `smsjobs.*` (Android SMS relay income) | Full | Not registered | None | - ---- - -## Summary - -**Strong, near-complete parity:** - -- MTProto edge, auth, sessions, PFS, 2FA/SRP -- Private chat messaging: send/edit/delete/forward/reply/reactions/scheduled/ - drafts/search/translation/albums/rich text -- Groups, supergroups, channels: admin model, invite links, forum topics, - discussion groups, slow mode, anti-spam toggles, public search -- Media pipeline with pluggable local-disk or S3 storage -- Stickers / custom emoji / GIFs (with an extended `@gif` catalog) -- Bots, inline mode, web apps / mini apps, games -- Stories (incl. layer 228 albums, stealth mode) -- Secret chats (E2E, server as blind relay) -- Dialogs/folders/chatlists and the full update-difference sync machinery -- Themes, wallpapers, peer colors, language packs - -**Partial / compatibility-first:** - -- Calls and live streams: signaling and state are complete; media relay needs - an operator-run SFU/TURN -- Boosts, channel/story statistics, communities, business features, conference - calls: core flows present, some perks and edges not gated -- Bot API HTTP gateway: a useful subset, not the full `api.telegram.org` -- Moderation: instance-level tooling exists, no global trust-and-safety network - -**Deliberately not implemented:** - -- The entire payments/economy layer: invoices, Telegram Stars, star gifts, - Premium purchase, TON/revenue withdrawal, Play/App Store billing. Read-only - RPCs return valid empty responses so clients don't break. -- External push notifications (APNs / FCM / GCM). Delivery is only over the - live MTProto connection; `registerDevice` records the in-connection push - session and ignores other token types. -- Multi-DC / CDN infrastructure: one logical DC, files always served from - origin, no DC-migration redirects. -- `contacts.getLocated` (nearby), takeout sessions, `smsjobs.*`, Instant View. - -**OwpenGram-only additions not in Telegram's server:** - -- WebAuthn / passkey sign-in -- Self-hosted "Login with Telegram" OpenID Connect provider -- Admin API + web UI, RBAC admin tokens, admin broadcast/announcements -- Shared-device detection -- Bundled TUI server-operations panel -- AI compose / rewrite with pluggable local or external providers -- Pluggable S3/MinIO media backend with live switch