feat: sync community aggregates

Sync telesrv 36eda30 (feat(communities): implement Layer 228 community aggregates).

Skipped telesrv docs changes per public sync rules; normalized the public appearance seed label.
This commit is contained in:
A 2026-07-20 16:46:02 +08:00
parent 7de1766941
commit da6a57e1a3
52 changed files with 5687 additions and 289 deletions

View file

@ -280,7 +280,10 @@ type ChannelBannedRights struct {
SendPlain bool
EditRank bool
SendReactions bool
UntilDate int
// ManageLinkedPeers is the Layer 228 default restriction used by Communities:
// true means only admins may add peers; false lets members submit requests.
ManageLinkedPeers bool
UntilDate int
}
// ChannelReactionPolicyType describes which reactions are allowed in a channel.
@ -431,6 +434,10 @@ type Channel struct {
// megagroup && (public || has_geo || has_link) 判定是否拉取候选列表。
HasLink bool
LinkedChatID int64
// LinkedCommunityID is the unique Community containing this group/channel.
// A channel can belong to at most one Community and Communities themselves
// are stored in a separate aggregate, never in channels.
LinkedCommunityID int64
// Monoforum 标记本频道是「频道私信(Direct Messages)」的 monoforum 虚拟频道。
// LinkedMonoforumID:母频道指向其 monoforum;monoforum 反向指向母频道(双向)。
Monoforum bool
@ -561,11 +568,15 @@ const (
ChannelActionStarGiftUnique ChannelMessageActionType = "star_gift_unique"
// ChannelActionSetChatWallpaper 映射 messageActionSetChatWallPaper:频道外观页设置 wallpaper。
ChannelActionSetChatWallpaper ChannelMessageActionType = "set_chat_wallpaper"
// ChannelActionChangeCommunity maps messageActionChangeCommunity. A non-zero
// CommunityID means linked; zero means unlinked.
ChannelActionChangeCommunity ChannelMessageActionType = "change_community"
)
// ChannelMessageAction describes a service action without depending on tg.*.
type ChannelMessageAction struct {
Type ChannelMessageActionType
CommunityID int64
Title string
IconColor int
IconEmojiID int64
@ -2039,18 +2050,24 @@ type ChannelSearchPostsRequest struct {
// ChannelGlobalSearchRequest describes a bounded messages.searchGlobal page
// over channel/supergroup messages visible to the current account.
type ChannelGlobalSearchRequest struct {
Query string
BroadcastsOnly bool
GroupsOnly bool
MusicOnly bool
HasFolderID bool
FolderID int
OffsetRate int
OffsetChannelID int64
OffsetID int
MinDate int
MaxDate int
Limit int
Query string
ChannelIDs []int64
RestrictChannelIDs bool
// AllowPublicPreview includes linked public channels that the account can
// preview without joining. It is enabled only by Layer 228 Community-scoped
// search; ordinary global search remains joined-dialog-only.
AllowPublicPreview bool
BroadcastsOnly bool
GroupsOnly bool
MusicOnly bool
HasFolderID bool
FolderID int
OffsetRate int
OffsetChannelID int64
OffsetID int
MinDate int
MaxDate int
Limit int
}
// ChannelRepliesFilter describes messages.getReplies query conditions.

View file

@ -0,0 +1,214 @@
package domain
import "errors"
const (
MaxCommunityPeers = 100
MaxCommunityBotPeers = 100
MaxCommunityLinkRequests = 100
MaxCommunityTitleRunes = 128
MaxCommunityAboutRunes = 255
MaxCommunityParticipants = 200
)
var (
ErrCommunityInvalid = errors.New("community invalid")
ErrCommunityPrivate = errors.New("community private")
ErrCommunityAdminRequired = errors.New("community admin required")
ErrCommunityCreatorRequired = errors.New("community creator required")
ErrCommunityPeerInvalid = errors.New("community peer invalid")
ErrCommunityPeerLinked = errors.New("community peer already linked")
ErrCommunityPeersTooMuch = errors.New("community peers too much")
ErrCommunityRequestCreated = errors.New("community request created")
ErrCommunityRequestMissing = errors.New("community request missing")
ErrCommunityParticipantInvalid = errors.New("community participant invalid")
)
// Community is the Layer 228 aggregation container. It intentionally has no
// message/read/pts fields: linked dialogs remain the only message truth.
type Community struct {
ID int64
AccessHash int64
CreatorUserID int64
Title string
About string
Date int
Deleted bool
DefaultBannedRights ChannelBannedRights
PhotoID int64
PhotoDCID int
PhotoStripped []byte
}
type CommunityMemberRole string
const (
CommunityRoleCreator CommunityMemberRole = "creator"
CommunityRoleAdmin CommunityMemberRole = "admin"
CommunityRoleMember CommunityMemberRole = "member"
)
type CommunityMemberStatus string
const (
CommunityMemberActive CommunityMemberStatus = "active"
CommunityMemberKicked CommunityMemberStatus = "kicked"
)
type CommunityMember struct {
CommunityID int64
UserID int64
Role CommunityMemberRole
Status CommunityMemberStatus
AdminRights ChannelAdminRights
Rank string
Date int
}
func (m CommunityMember) Active() bool { return m.Status == CommunityMemberActive }
func (m CommunityMember) CanManageLinkedPeers() bool {
return m.Active() && (m.Role == CommunityRoleCreator ||
(m.Role == CommunityRoleAdmin && m.AdminRights.ManageLinkedPeers))
}
func (m CommunityMember) CanChangeInfo() bool {
return m.Active() && (m.Role == CommunityRoleCreator ||
(m.Role == CommunityRoleAdmin && m.AdminRights.ChangeInfo))
}
func (m CommunityMember) CanAddAdmins() bool {
return m.Active() && (m.Role == CommunityRoleCreator ||
(m.Role == CommunityRoleAdmin && m.AdminRights.AddAdmins))
}
func (m CommunityMember) CanBanUsers() bool {
return m.Active() && (m.Role == CommunityRoleCreator ||
(m.Role == CommunityRoleAdmin && m.AdminRights.BanUsers))
}
type CommunityPeerVisibility string
const (
CommunityPeerVisible CommunityPeerVisibility = "visible"
CommunityPeerHidden CommunityPeerVisibility = "hidden"
)
type CommunityPeerLink struct {
CommunityID int64
Peer Peer
Visibility CommunityPeerVisibility
CanViewHistory bool
CreatedBy int64
Date int
}
func (l CommunityPeerLink) Visible() bool { return l.Visibility == CommunityPeerVisible }
type CommunityPeerLinkRequest struct {
CommunityID int64
Peer Peer
RequestedBy int64
Visibility CommunityPeerVisibility
Date int
}
type CommunityUserState struct {
CommunityID int64
UserID int64
Collapsed bool
Pinned bool
PinnedOrder int
NotifySettings *PeerNotifySettings
}
type CommunityView struct {
Community Community
Self CommunityMember
State CommunityUserState
Links []CommunityPeerLink
Channels []Channel
Users []User
ServiceMessages []SendChannelMessageResult
AdminsCount int
KickedCount int
PendingRequests int
Forbidden bool
}
func (v CommunityView) Creator() bool {
return v.Self.Active() && v.Self.Role == CommunityRoleCreator
}
type CreateCommunityRequest struct {
CreatorUserID int64
Title string
About string
InitialPeer Peer
Visibility CommunityPeerVisibility
Date int
}
type CommunityTogglePeerLinkRequest struct {
ActorUserID int64
CommunityID int64
Peer Peer
Visibility CommunityPeerVisibility
Deleted bool
RequestOnly bool
Date int
}
type CommunityTogglePeerLinkResult struct {
Community Community
Peer Peer
RequestedBy int64
Link *CommunityPeerLink
ServiceMessage *SendChannelMessageResult
Removed bool
RequestCreated bool
}
type CommunityPeerLinkRequestPage struct {
TotalCount int
Requests []CommunityPeerLinkRequest
NextOffset string
Channels []Channel
Users []User
}
type CommunityParticipantJoinedChats struct {
CreatorChatIDs []int64
JoinedChatIDs []int64
Channels []Channel
Users []User
}
type CommunityParticipantList struct {
Community Community
Count int
Participants []CommunityMember
Users []User
Hash int64
}
type CommunityParticipantBanResult struct {
Changed bool
ChannelBans []EditChannelBannedResult
RemovedLinks []CommunityTogglePeerLinkResult
}
type CommunityEditAdminRequest struct {
ActorUserID int64
CommunityID int64
UserID int64
Rights ChannelAdminRights
Rank string
Date int
}
type CommunitySearchScope struct {
CommunityID int64
ChannelIDs []int64
BotUserIDs []int64
}

View file

@ -6,6 +6,9 @@ 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"
@ -175,6 +178,7 @@ type DialogList struct {
ChannelMessages []ChannelMessage
Users []User
Channels []Channel
Communities []CommunityView
State UpdateState
Hash int64
Count int

View file

@ -242,6 +242,10 @@ type MessageFilter struct {
// SavedPeer 非零时仅返回 self-chat 中该 saved 子会话的消息
// (messages.getSavedHistory);Peer 必须同时是 self。
SavedPeer Peer
// PeerIDs restricts a global private search to these user peers. Empty is a
// valid restricted set, so RestrictPeerIDs carries presence separately.
PeerIDs []int64
RestrictPeerIDs bool
}
// SendPrivateTextRequest 是私聊文本/媒体发送命令。

View file

@ -124,6 +124,9 @@ type User struct {
// PersonalChannelID 是资料页展示的「个人频道」(account.updatePersonalChannel);
// 0 表示未设置。资料投影时按它取频道对象与最新一帖。
PersonalChannelID int64
// LinkedCommunityID is the single Community containing this bot. Ordinary
// users must keep it zero; the community aggregate enforces that invariant.
LinkedCommunityID int64
Color PeerColor
ProfileColor PeerColor
// Profile photo fields are filled by app-layer user projection. PhotoID==0 表示无头像。