Commit graph

417 commits

Author SHA1 Message Date
655fdacb0a Merge branch 'docs/telegram-feature-comparison' 2026-09-13 17:59:30 +01:00
9353953cf4 docs: compare OwpenGram Server's implemented features to Telegram's official server
Feature-by-feature comparison across transport/auth, messaging, groups,
media, bots, calls, stories, secret chats, sync, themes, moderation, and
the payments/Stars economy - marking each Full / Partial / Stub / None,
with the deliberate omissions and OwpenGram-only additions called out
separately.
2026-09-13 17:59:24 +01:00
b71f8d7fa7 Merge branch 'fix/smtp-date-messageid-headers' 2026-09-11 17:40:03 +01:00
2e96d9490e smtp: add Date and Message-ID headers to outgoing mail
RFC 5322 requires both headers. Date uses RFC1123Z formatting; Message-ID
is a random 16-byte token scoped to the sending domain parsed from From.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 17:39:55 +01:00
2d42158ec5 Merge branch 'fix/reserved-usernames-checkusername' 2026-09-09 21:41:53 +01:00
367f2be59c usernames: report reserved names as taken in the check paths too
account.checkUsername / channels.checkUsername / bots.checkUsername said a
reserved name was available and only updateUsername rejected it. Add the
blocklist check to peerUsernameAvailable (covers account + channel, both
backends) and to bots.Service.CheckUsername, so the client shows "username is
taken" immediately.
2026-09-09 21:41:53 +01:00
5b2f68c61e Merge branch 'fix/reserved-usernames-actionflow' 2026-09-09 21:23:25 +01:00
90af881867 admin ui: reserved usernames use the standard dry-run/confirm flow
Both reserve and unreserve go through ActionButton now (reason -> dry-run ->
confirm, journalled) like every other admin action. The reserve modal keeps
just the username field and hands off; it autofocuses and echoes @<name> live
so the field being filled is unambiguous.
2026-09-09 21:23:25 +01:00
37a3462a22 Merge branch 'fix/reserved-usernames-modal' 2026-09-09 21:15:23 +01:00
4e2cf7c5ac admin ui: self-contained reserve-username modal, plain @ text
The reserve modal delegated to a nested ActionButton, whose own flow modal
opened over it - the username field ended up behind it and the request preview
came through empty on confirm. Replace it with a modal that owns its username
and reason fields and posts the reserve/unreserve command directly. Render the
@ prefix as text, not an icon.
2026-09-09 21:15:23 +01:00
93a4ed2bca Merge branch 'fix/reserved-usernames-layout' 2026-09-09 21:11:52 +01:00
241fd442fb admin ui: match the reserved-usernames page layout to the NFT page
Move "Reserve username" into a modal opened from the page actions, and keep a
single search toolbar in the query panel, so the page matches Collectible
Usernames instead of stacking two toolbars with an unconstrained input.
2026-09-09 21:11:52 +01:00
7dbb931b31 Merge branch 'feat/reserved-usernames' 2026-09-09 19:57:14 +01:00
65aaa263b1 usernames: operator reserved-username blocklist
A plain blocklist for names like @support - separate from the collectible
system, so a reservation has no owner, no price and no "bought on Fragment"
badge.

- reserved_usernames table + migration.
- Enforced in replacePeerUsernameTx (the single editable-username write point:
  account.updateUsername, channels.updateUsername, @BotFather /setusername) and
  in the collectible mint path; a reserved name returns USERNAME_OCCUPIED.
- admin.Service: ReserveUsername / UnreserveUsername (journalled commands) and
  the ReservedUsernames listing.
- adminapi: /v1/reserved-usernames{,/reserve,/unreserve}.
- telesrv-admin panel + a "Reserved Usernames" page in the web UI (dist rebuilt).
- Postgres and in-memory store implementations; the memory registry gains an
  optional reserved-name check so tests exercise the same rule.
2026-09-09 19:57:14 +01:00
515038aace Merge branch 'feat/botapi-chat-members' 2026-09-09 16:26:33 +01:00
d9ee8cbee0 botapi: getChatMemberCount, getChatMember, and a fuller getChat
- getChatMemberCount: channel/supergroup participant count (numeric chat_id).
- getChatMember: resolves a member via GetParticipant, projected to a Bot API
  ChatMember (creator/administrator/restricted/member/left/kicked with the
  matching rights); a user simply not in an accessible chat returns "left".
- getChat now uses the full channel view and adds permissions (from the default
  restrictions), slow_mode_delay, linked_chat_id and pinned_message.

Channel-only methods reject user chat_ids; private chats the bot cannot access
return CHAT_NOT_FOUND.
2026-09-09 16:26:33 +01:00
71da34607e Merge branch 'feat/botapi-getchat' 2026-09-09 16:14:49 +01:00
578eb5a1f6 botapi: implement getChat
Adds the getChat method to the HTTP Bot API gateway. Numeric chat_id only (no
@username). Resolution goes through the shared peer resolvers:

- user: ByID; unknown -> CHAT_NOT_FOUND
- channel/supergroup: ResolveChannel, so a public one resolves even when the bot
  is not a member (projected as a preview); a private one the bot cannot access
  -> CHAT_NOT_FOUND, a banned bot likewise

The Chat projection returns id (bot-api encoded), type ("channel" for a
broadcast, "supergroup" for a megagroup, "private" for a user), title, username,
first/last name, description, is_forum, and the scam/fake/verified flags.
2026-09-09 16:14:49 +01:00
3229bfa6b3 Merge branch 'chore/build-sh-port-2500' 2026-09-09 16:11:07 +01:00
36d0f19767 build: map port 2500 on the pod 2026-09-09 16:11:07 +01:00
25008fddc5 Merge branch 'chore/build-sh-pod-create' 2026-09-09 15:07:25 +01:00
284d8be365 build: create the pod with its port mappings if missing
Instead of erroring when the owpengram pod is absent, build.sh now creates it
with the MTProto (2398), admin (2600), extra TCP (2400) and RTC/UDP port
mappings.
2026-09-09 15:07:25 +01:00
485323dbfa Merge branch 'chore/build-sh-deploy' 2026-09-09 15:06:19 +01:00
f940d3403b build: recreate and start the pod containers after building
build.sh now, after building the image, recreates owpengram-server and
owpengram-admin in the pod (podman create --replace) and starts them. Guards
that the pod and .env exist; NO_DEPLOY=1 keeps the old build-only behaviour,
POD overrides the pod name.
2026-09-09 15:06:19 +01:00
0e2fc6705b Merge branch 'fix/botfather-start-manage-bot' 2026-09-09 15:03:53 +01:00
a3fc5ba3b5 botfather: /start <bot> opens that bot's menu
The "Manage Bot" button on a bot's profile deep-links to @BotFather with
start=<bot username>. parseBotCommand dropped the argument, so /start <bot>
just replied with the generic greeting instead of the per-bot menu.

Route "/start <arg>" to the bot's "What do you want to do?" screen (same as
/mybots then tapping the bot) when <arg> names one of the sender's own bots by
username or id; empty or unknown args keep the greeting.
2026-09-09 15:03:53 +01:00
f478c87a56 Merge branch 'fix/admin-accounts-list-sessionless' 2026-09-09 14:56:10 +01:00
c939f7c92e admin: list accounts that have no active sessions
The Accounts tab (readStore.ListAccounts) inner-joined the authorizations
aggregate, so any account with zero authorization rows was silently hidden -
accounts that never finished login, had all sessions revoked, or were frozen
then unfrozen. CountAccounts and SearchAccounts already LEFT JOIN, so the count
and search disagreed with the list.

Switch ListAccounts to LEFT JOIN auth and COALESCE the null last_active_at /
device_count (sessionless accounts sort last), matching SearchAccounts.
2026-09-09 14:56:10 +01:00
686adc1e10 Merge branch 'fix/forum-topic-replies-preview' 2026-09-09 14:45:06 +01:00
7083faa786 forum: let non-members preview topic replies in a public channel
ListChannelReplies used getChannelForMemberOrLinkedGuest, so messages.getReplies
was member-only. ListChannelHistory (flat history) uses getChannelForViewer and
already allows a public channel's non-members to preview it. The mismatch meant
that on a public forum you could preview the flat history but not the topics -
and after leaving, tdesktop's topic view got CHANNEL_PRIVATE and sat on
"Loading..." forever instead of rendering a preview.

Switch the primary channel lookup in ListChannelReplies (both stores) to the
viewer-scope path. Private channels still return CHANNEL_PRIVATE to non-members;
the broadcast comment-thread lookup is unchanged.
2026-09-09 14:43:59 +01:00
7d280b6f8a Merge branch 'build-metadata-stamping' 2026-09-09 14:27:28 +01:00
1c477fe32a build: stamp git metadata into the container image
.containerignore excludes .git, so go build's automatic VCS stamping produced
nothing and telesrv logged git_commit/git_branch/git_tree_state/build_time as
"unknown" on startup.

- Containerfile: accept GIT_COMMIT/GIT_BRANCH/GIT_TREE_STATE/BUILD_TIME build
  args and pass them to the gramsrv build via -ldflags -X.
- build.sh: wrapper that fills those args from the current checkout and runs
  podman build.
2026-09-09 14:26:58 +01:00
31a6ab4887 Merge pull request 'forum: project the forum's own channel with member state in getForumTopics' (#5) from fix/forum-topics-nonmember-left into main
Reviewed-on: #5
2026-09-09 14:53:56 +02:00
f94edc6cad forum: project the forum's own channel with member state in getForumTopics
messages.getForumTopics returned every chat via tgChannels -> tgChannelChatMin,
so the forum's own channel came back as a min object with left unset. A client
with no other object for that peer (a fresh account browsing a public forum by
username) then rendered the forum as already joined: topic list visible, no
Join button, but no messages.

Render the primary channel with tgChannelChatForView so a non-member preview
carries left=true; keep the other referenced channels as min.
2026-09-09 13:53:20 +01:00
5f65a47d89 Merge pull request 'welcome message: point updates-channel mention at @ziodotsh' (#4) from welcome-mention-ziodotsh into main
Reviewed-on: #4
2026-09-09 14:49:49 +02:00
7b662ad647 welcome message: point updates-channel mention at @ziodotsh
Rename the official updates channel mention from @zio to @ziodotsh in the
welcome message template and update the affected send-message test.
2026-09-09 13:49:07 +01:00
be3623797e Merge pull request 'channels: drop stale membership caches on join/leave' (#3) from fix/stale-membership-after-leave into main
Reviewed-on: #3
2026-09-09 14:34:04 +02:00
55a6e0bb35 channels: drop stale membership caches on join/leave
After channels.leaveChannel, a client that polls channels.getFullChannel kept
receiving a projection that still showed it as an active member (left=false)
until the per-(viewer,channel) RPC projection cache and the store-level member
cache lapsed on their own or the async read-model NOTIFY landed. The client
therefore kept an open compose box while every send was already rejected with
CHANNEL_PRIVATE - most visible on public forum supergroups, where getFullChannel
keeps succeeding via the preview path instead of tearing the chat down.

Every other membership-mutating path already busts these caches synchronously;
join/leave/invite/request-approval did not. Add:

- store: invalidateChannelMembershipCaches (row + member + dialog caches),
  called post-commit from JoinChannel, LeaveChannel, ImportInvite,
  InviteToChannel.
- rpc: invalidateChannelMembershipProjection (channelFullProjectionCache pair),
  called from the join/leave/invite/hide-requests handlers for every user whose
  membership changed.
2026-09-09 13:28:50 +01:00
2c782aab95 Merge pull request 'channels: force pre-history visible when a group gets a public username' (#2) from fix/prehistory-visible-on-publish into main
Reviewed-on: #2
2026-09-09 13:52:52 +02:00
879ff48b49 channels: force pre-history visible when a group gets a public username
New supergroups are created with "chat history for new members" hidden (the
client sets this right after creation, matching official Telegram). The
official server then forces it back to visible when the group is made public;
owpengram's UpdateUsername left the flag alone, leaving public groups in a
state where non-members (and post-join members) see no history at all.

UpdateUsername now clears pre_history_hidden whenever a non-empty username is
assigned, in the same transaction, with a matching admin-log event. Removing
the username leaves the flag untouched, so the creator can hide history again
once the group is private.
2026-09-09 12:51:23 +01:00
7daaaf164f Merge pull request 'channels: give getParticipants a stable Hash when read-model versions are missing' (#1) from fix/channel-participants-fallback-hash into main
Reviewed-on: astra/owpengram#1
2026-09-09 13:27:20 +02:00
045f39c3f9 channels: give getParticipants a stable Hash when read-model versions are missing
cachedParticipants returned a participant page with Hash=0 whenever the
channel_base / channel_participants rows in read_model_versions were never
seeded for a channel (e.g. groups created via messages.createChat). With
Hash=0 the RPC layer can never answer channels.channelParticipantsNotModified,
so a client that polls the member list re-fetches it in a tight loop forever.

Fall back to a deterministic content hash derived from the page itself
(channel id, page key, count, and each member's id/role/status/rank) so an
unchanged member list yields an identical non-zero Hash and the client
converges. The read-model-backed path is unchanged.
2026-09-09 12:26:30 +01:00
bf22bd4006 forum: fix reply_to_top_id for replies inside a forum
resolveChannelReply applied discussion-thread logic (reply_to_top_id =
the replied-to message's own id) to forum replies. Replying to a General
message produced reply_to_top_id = <that id>, a topic no client can
resolve: the reply vanished from every topic view and reply-jump on
strict clients said "message doesn't exist".

Forum replies now inherit the target's topic via domain.ForumReplyTopicID
(target's topic, or its own id if it's a topic-create, else General), and
General (topic 1) is accepted as a valid virtual topic everywhere, so
sends carrying top_msg_id: 1 are no longer rejected. Non-forum discussion
threads are unchanged.
2026-09-09 11:15:56 +01:00
6d34843fd0 forum: let non-members browse a public forum's topic list
ListForumTopics / GetForumTopicsByID / GeneralForumTopic gated on
membership while channel history uses the public-preview path, so a
public forum's topics (General included) were invisible until you joined.
Switch them to getChannelForViewer / channelForViewerLocked; private
forums and write paths keep the membership gate.
2026-09-09 11:15:56 +01:00
f26468ef6d media: never project zero image/video dimensions (crashes Telegram Desktop on reactions) 2026-09-08 16:29:37 +01:00
233a2399e9 welcome message: link @zio with a mention entity 2026-09-08 16:29:37 +01:00
f8aefb7fd5 payments: stub getStarGifts / getSavedStarGifts to stop tdesktop 500 retry storm 2026-09-08 16:29:37 +01:00
b3281d66d7 Containerfile: copy sticker-seed data into the image 2026-09-08 13:55:30 +01:00
8bc54fbfc5 /mybots: Edit Bot summary screen, return-to-menu, clickable @mentions
- Edit Bot now shows the current value of every field (Name/About/
  Description/Botpic/Commands) like BotFather, with real botpic status
  via a new PeerHasAvatar port method.
- After editing a field the dialog lands back on a fresh Edit Bot menu
  (working "Back to bot" / "Bots list" buttons) instead of ending, so a
  follow-up button press no longer reports the button as expired.
- Service-bot messages now render @username as a tappable mention entity.
2026-09-08 13:44:01 +01:00
165f81414c Add @zio channel invite to the sign-in welcome message 2026-09-08 13:31:39 +01:00