package domain // PeerType 标识 dialog 所属 peer 类型。 type PeerType string const ( PeerTypeUser PeerType = "user" PeerTypeChannel PeerType = "channel" // PeerTypeCommunity identifies a Layer 228 Community container. Communities // have dialog pin/notify state but never own messages, read boundaries or pts. PeerTypeCommunity PeerType = "community" // PeerTypeFolder 仅用于 dialog 置顶事件中表达 dialogPeerFolder // (archive folder 行本身被置顶/取消置顶),ID 为 folder_id。 PeerTypeFolder PeerType = "folder" ) const ( // DialogMainFolderID 是 TDesktop 主会话列表 folder_id。 DialogMainFolderID = 0 // DialogArchiveFolderID 是 Telegram 约定的归档会话 folder_id。 DialogArchiveFolderID = 1 // DialogCustomFolderMinID 起才允许用户自定义 filter。 DialogCustomFolderMinID = 2 // MaxDialogFolders 限制单用户自定义 filter 数量,避免无界配置拖垮启动同步。 MaxDialogFolders = 100 // MaxDialogFolderPeers 限制单 filter 中 include/exclude/pinned peer 数。 MaxDialogFolderPeers = 100 // MaxDialogFolderTitleRunes 对齐 Telegram folder title 的短标题语义。 MaxDialogFolderTitleRunes = 64 // MaxDialogDraftsPerUser bounds messages.getAllDrafts / clearAllDrafts work. MaxDialogDraftsPerUser = 1000 // MaxPinnedDialogsMainFolder/MaxPinnedDialogsArchiveFolder 对齐 TDesktop // 内置默认限额(appConfig dialogs_pinned_limit_default=5 / // dialogs_folder_pinned_limit_default=100);超限返回 // PINNED_DIALOGS_TOO_MUCH,服务端兜底防止客户端截断后两端漂移。 MaxPinnedDialogsMainFolder = 5 MaxPinnedDialogsArchiveFolder = 100 // premium 档对齐 appConfig dialogs_pinned_limit_premium=10 / // dialogs_folder_pinned_limit_premium=200。服务端档位必须 ≥ 客户端宣告 // 档位,否则 premium 用户 pin 第 6 个会话会被拒、UI 回滚。 MaxPinnedDialogsMainFolderPremium = 10 MaxPinnedDialogsArchiveFolderPremium = 200 ) // PinnedDialogsLimit 返回 folder 维度的置顶上限档位(premium 双档)。 func PinnedDialogsLimit(folderID int, premium bool) int { if folderID == DialogArchiveFolderID { if premium { return MaxPinnedDialogsArchiveFolderPremium } return MaxPinnedDialogsArchiveFolder } if premium { return MaxPinnedDialogsMainFolderPremium } return MaxPinnedDialogsMainFolder } // Peer 是业务层 peer 值对象,不依赖 TL 类型。 type Peer struct { Type PeerType ID int64 } // IsSelfUser reports whether this peer is the current user's own user peer. func (p Peer) IsSelfUser(userID int64) bool { return userID != 0 && p.Type == PeerTypeUser && p.ID == userID } // Dialog 是账号的一条会话摘要。 type Dialog struct { Peer Peer ChannelLeft bool FolderID int TopMessage int TopMessageDate int // HistoryClearAnchorID/Date are internal owner-view projection metadata. // They are not TL dialog fields; dialog response assembly uses them to // materialize the matching messageActionHistoryClear top payload. HistoryClearAnchorID int HistoryClearAnchorDate int ReadInboxMaxID int ReadOutboxMaxID int UnreadCount int UnreadMentions int UnreadReactions int TTLPeriod int ThemeEmoticon string HasScheduled bool Pinned bool PinnedOrder int UnreadMark bool ViewForumAsMessages bool PeerSettingsBarHidden bool // TopMessageMentioned/MediaUnread/UnreadProjected are internal owner-view // facts for materialized channel dialog snapshots. They are not TL dialog // fields; response assembly applies them to the shared top-message payload. TopMessageMentioned bool TopMessageMediaUnread bool TopMessageUnreadProjected bool // DefaultSendAs is internal owner-view channel dialog metadata. It is kept // in the materialized owner snapshot so warming the exact viewer/channel // projection cannot erase channels.setDefaultSendAs state. DefaultSendAs *Peer // ChannelMember is the internal exact access/read projection captured with // a materialized channel dialog. It is never a TL dialog field. Keeping it // under the same dialog_owner generation lets startup warm permission reads // before channel difference without a per-channel PostgreSQL lookup. ChannelMember *ChannelMember // Pts 是 channel peer 当前 channel pts;客户端用 dialog.pts 初始化本地 // channel 序列并决定 getChannelDifference 起点,channel dialog 必填。 Pts int Draft *DialogDraft // NotifySettings 是该会话的 per-peer 通知设置(nil=未配置,投影时回落默认)。 // 由读路径批量装配(非 dialog store 持久字段),见 rpc.withDialogNotifySettings。 NotifySettings *PeerNotifySettings } // DialogDraftWebPage stores a draft link preview without depending on TL input media types. type DialogDraftWebPage struct { URL string ForceLargeMedia bool ForceSmallMedia bool Optional bool } type SuggestedPostPriceKind string const ( SuggestedPostPriceStars SuggestedPostPriceKind = "stars" SuggestedPostPriceTON SuggestedPostPriceKind = "ton" ) // SuggestedPostPrice is either a decimal Stars amount or a nanotons amount. type SuggestedPostPrice struct { Kind SuggestedPostPriceKind Amount int64 Nanos int } // SuggestedPost is the domain-only snapshot shared by a monoforum message and its cloud draft. type SuggestedPost struct { Accepted bool Rejected bool Price *SuggestedPostPrice ScheduleDate int } // DialogDraft is a cloud draft for one peer/topic, expressed only in domain types. type DialogDraft struct { Peer Peer TopMessageID int Date int NoWebpage bool InvertMedia bool Message string Entities []MessageEntity ReplyTo *MessageReply WebPage *DialogDraftWebPage Effect int64 SuggestedPost *SuggestedPost RichMessage *MessageRichMessage } // Empty reports whether this draft should clear the cloud draft slot. func (d DialogDraft) Empty() bool { replyOnlyTopic := d.ReplyTo != nil && d.ReplyTo.MessageID == 0 && d.ReplyTo.TopMessageID > 0 return !d.NoWebpage && !d.InvertMedia && d.Message == "" && len(d.Entities) == 0 && (d.ReplyTo == nil || replyOnlyTopic) && d.WebPage == nil && d.Effect == 0 && d.SuggestedPost == nil && d.RichMessage.IsZero() } // DialogArchiveSummary 聚合归档(folder_id=1)状态,供主列表 getDialogs // 第一页输出 dialogFolder 条目:TDesktop 依赖该条目发现 archive 的存在 // (新登录设备没有任何 update 可重放,缺少它归档会话将彻底不可见)。 type DialogArchiveSummary struct { // TopPeer/TopMessage 是归档内最新会话及其 top 消息(dialogFolder.peer/top_message)。 TopPeer Peer TopMessage int // TopDialog retains the owner projection needed to hydrate a channel archive // top payload without re-reading channel_members/channel_dialogs. It is an // internal read-model field and is never converted into a second TL dialog. TopDialog *Dialog // UnreadPeersCount 是归档内有未读(或手动标记未读)的会话数; // UnreadMessagesCount 是归档未读消息总数。当前未接 per-peer mute // 状态,全部计入 unmuted 桶。 UnreadPeersCount int UnreadMessagesCount int Pinned bool } // DialogList 是 dialogs 查询结果。 type DialogList struct { Dialogs []Dialog Messages []Message ChannelMessages []ChannelMessage Users []User Channels []Channel Communities []CommunityView State UpdateState Hash int64 Count int // ArchiveSummary 非 nil 时,主列表响应头部追加 dialogFolder 条目。 ArchiveSummary *DialogArchiveSummary } // DialogHashCheck reports whether a cached stable dialog list hash is known. type DialogHashCheck struct { Known bool Matched bool Hash int64 Count int } // DialogFilter 是会话列表查询条件。 type DialogFilter struct { PinnedOnly bool ExcludePinned bool HasFolderID bool FolderID int Folder *DialogFolder OffsetDate int OffsetID int HasOffsetPeer bool OffsetPeer Peer Limit int Hash int64 } // DialogFolderPeer 是 folder/filter 规则中的 peer,保留 access_hash 供 RPC 层回写 InputPeer。 type DialogFolderPeer struct { Peer Peer AccessHash int64 } // DialogFolder 是用户自定义会话分组规则。它只表达业务含义,不依赖 TL 生成类型。 type DialogFolder struct { ID int Contacts bool NonContacts bool Groups bool Broadcasts bool Bots bool ExcludeMuted bool ExcludeRead bool ExcludeArchived bool TitleNoanimate bool Title string TitleEntities []MessageEntity Emoticon string HasEmoticon bool Color int HasColor bool HasMyInvites bool PinnedPeers []DialogFolderPeer IncludePeers []DialogFolderPeer ExcludePeers []DialogFolderPeer IsChatlist bool } // DialogFolderList 是 messages.getDialogFilters 的业务响应。 type DialogFolderList struct { TagsEnabled bool Folders []DialogFolder } // FolderPeerUpdate 描述 folders.editPeerFolders 的单个归档/还原变更。 type FolderPeerUpdate struct { Peer Peer FolderID int }