feat: sync recent call and channel fixes

This commit is contained in:
A 2026-07-01 14:34:59 +08:00
parent e5e0080216
commit 866a87583e
65 changed files with 6680 additions and 229 deletions

View file

@ -204,6 +204,40 @@ type ChannelAdminRights struct {
ManageDirectMessages bool
}
// CreatorChannelAdminRights returns the full rights set clients expect on creator projections.
func CreatorChannelAdminRights() ChannelAdminRights {
return ChannelAdminRights{
ChangeInfo: true,
PostMessages: true,
EditMessages: true,
DeleteMessages: true,
PostStories: true,
EditStories: true,
DeleteStories: true,
BanUsers: true,
InviteUsers: true,
PinMessages: true,
AddAdmins: true,
ManageCall: true,
ManageRanks: true,
}
}
// NormalizeFullMegagroupAdminRights fills implicit full-admin bits for megagroups.
func NormalizeFullMegagroupAdminRights(ch Channel, rights ChannelAdminRights) ChannelAdminRights {
if ch.Megagroup && !ch.Broadcast &&
rights.ChangeInfo &&
rights.DeleteMessages &&
rights.BanUsers &&
rights.InviteUsers &&
rights.PinMessages &&
rights.AddAdmins &&
rights.ManageCall {
rights.ManageRanks = true
}
return rights
}
// ChannelBannedRights is a domain-only representation of Telegram banned rights.
type ChannelBannedRights struct {
ViewMessages bool
@ -1019,6 +1053,9 @@ type ChannelRecommendationsResult struct {
}
// PublicChannelSearchResult contains contacts.search public channel/supergroup matches.
// Results are public peers the viewer is not an active member of; joined peers
// are intentionally left to dialogs/resolve paths so clients do not render
// invisible discovery rows for already joined channels.
type PublicChannelSearchResult struct {
MyResults []Channel
Results []Channel
@ -1203,6 +1240,26 @@ type EditChannelAdminResult struct {
Date int
}
// TransferChannelOwnershipRequest transfers a channel/supergroup to another active member.
type TransferChannelOwnershipRequest struct {
UserID int64
ChannelID int64
NewOwnerID int64
Date int
}
// TransferChannelOwnershipResult describes both participant transitions produced by an owner transfer.
type TransferChannelOwnershipResult struct {
Channel Channel
PreviousOwner ChannelMember
OldOwner ChannelMember
PreviousNewOwner ChannelMember
NewOwner ChannelMember
Events []ChannelUpdateEvent
Recipients []int64
Date int
}
// EditChannelMemberRankRequest sets or clears a participant's member tag (rank)
// without touching their role or admin rights.
type EditChannelMemberRankRequest struct {

View file

@ -66,7 +66,7 @@ type ImportContactsResult struct {
// UserSearchResult 是 contacts.search 的业务结果。
// MyResults 放当前账号通讯录内命中的用户;Results 放其他全局命中用户。
// MyChannelResults 放当前账号已加入的公开 channel/supergroup;ChannelResults 放其他公开命中。
// ChannelResults 放未加入的公开 channel/supergroup 命中;已加入频道由 dialogs/resolve 路径呈现。
type UserSearchResult struct {
MyResults []User
Results []User

View file

@ -14,6 +14,27 @@ const (
GroupCallStateDiscarded GroupCallState = "discarded"
)
// GroupCallKind distinguishes regular channel group calls from ad-hoc
// conference calls. Conference calls have no channel membership scope; access is
// granted by creator/participant/invite/slug.
type GroupCallKind string
const (
GroupCallKindChannel GroupCallKind = "channel"
GroupCallKindConference GroupCallKind = "conference"
)
// GroupCallInviteStatus is the durable state of a private conference invite.
type GroupCallInviteStatus string
const (
GroupCallInvitePending GroupCallInviteStatus = "pending"
GroupCallInviteAccepted GroupCallInviteStatus = "accepted"
GroupCallInviteDeclined GroupCallInviteStatus = "declined"
GroupCallInviteMissed GroupCallInviteStatus = "missed"
GroupCallInviteRevoked GroupCallInviteStatus = "revoked"
)
// 群通话业务错误;rpc 层映射为 GROUPCALL_* RPC_ERROR。
var (
ErrGroupCallInvalid = errors.New("group call invalid")
@ -21,6 +42,7 @@ var (
ErrGroupCallAlreadyStarted = errors.New("group call already started")
ErrGroupCallSSRCDuplicate = errors.New("group call ssrc duplicate")
ErrGroupCallNotJoined = errors.New("group call participant missing")
ErrConferenceChainInvalid = errors.New("conference call chain invalid")
)
// GroupCall 是一场群通话的权威态。
@ -29,6 +51,7 @@ type GroupCall struct {
AccessHash int64
ChannelID int64
CreatorUserID int64
Kind GroupCallKind
State GroupCallState
Title string
JoinMuted bool
@ -41,6 +64,14 @@ type GroupCall struct {
// StartedMsgID 是 messageActionGroupCall(started) 的频道消息 id(discard 时
// 客户端用它定位起始服务消息,当前仅记录)。
StartedMsgID int
// InviteSlug/InviteLink 仅 conference 使用。slug 必须在 migrate reason 与
// InputGroupCallSlug 中稳定可解析;link 是客户端 UI 复制/分享用完整 URL。
InviteSlug string
InviteLink string
// RandomID 是 conference create 的幂等键(creator_user_id + random_id)。
RandomID int64
// MigratedFromPhoneCallID 记录由哪通 P2P call 升级而来;link-only create 为 0。
MigratedFromPhoneCallID int64
}
// Active 报告通话是否仍在进行。
@ -48,6 +79,10 @@ func (c GroupCall) Active() bool {
return c.State == GroupCallStateActive
}
func (c GroupCall) Conference() bool {
return c.Kind == GroupCallKindConference
}
// GroupCallParticipant 是房间内一名参与者。
type GroupCallParticipant struct {
CallID int64
@ -67,7 +102,11 @@ type GroupCallParticipant struct {
// 快照(M3/M4 启用;M0/M1 仅透明保存 self-edit,不转发)。
VideoJSON []byte
PresentationJSON []byte
Left bool
// PublicKey/JoinBlock 仅 conference 使用。服务端不解析 E2E 内容,只持久化
// opaque bytes 并供 chain block 补拉/转发。
PublicKey []byte
JoinBlock []byte
Left bool
// LastCheckDate 是 checkGroupCall 保活水位。注意:客户端只在 Connecting 态
// 发 checkGroupCall(媒体连通后心跳停止),掉线判定必须与 SFU 媒体面活性
// 取双过期(见 sweeper),绝不能单凭此字段。
@ -85,11 +124,13 @@ type CreateGroupCallRequest struct {
// JoinGroupCallRequest 加入/重进群通话(rejoin 同主键换新 ssrc)。
type JoinGroupCallRequest struct {
CallID int64
UserID int64
SSRC int64
Muted bool
IsAdmin bool
CallID int64
UserID int64
SSRC int64
Muted bool
IsAdmin bool
PublicKey []byte
JoinBlock []byte
// VideoJSON 是本次 join 铸造的视频内部状态(endpoint+源组+active);rejoin
// 整体替换并**清空旧 PresentationJSON**(客户端主连接 rejoin 后会重发
// joinGroupCallPresentation,旧屏幕登记必须作废)。
@ -104,6 +145,27 @@ type GroupCallMutation struct {
Participant GroupCallParticipant
}
// RemoveConferenceCallParticipantsRequest describes a conference participant
// removal from both the media participant set and the E2E member chain.
type RemoveConferenceCallParticipantsRequest struct {
CallID int64
AuthorUserID int64
TargetUserIDs []int64
OnlyLeft bool
Kick bool
Block []byte
Now int
}
// RemoveConferenceCallParticipantsResult is the transactional result of
// accepting a conference E2E removal block and/or kicking active participants.
type RemoveConferenceCallParticipantsResult struct {
Call GroupCall
ParticipantsChanged []GroupCallParticipant
ChainBlock GroupCallChainBlock
ChainBlockAppended bool
}
// GroupCallParticipantUpdate 是 editGroupCallParticipant 的字段级更新(nil=不动)。
type GroupCallParticipantUpdate struct {
Muted *bool
@ -129,3 +191,33 @@ type GroupCallParticipantPage struct {
NextOffset string
Version int
}
// GroupCallInvite 是 conference call 在私聊中发出的邀请服务消息索引。
type GroupCallInvite struct {
CallID int64
InviterUserID int64
InviteeUserID int64
MessageID int
Status GroupCallInviteStatus
Video bool
CreatedAt int
UpdatedAt int
}
// GroupCallChainBlockLatestOffset 是客户端用来请求当前 sub-chain 最新 block 的哨兵值。
const GroupCallChainBlockLatestOffset = -1
// GroupCallChainBlock 是 conference E2E chain 的 opaque block。
type GroupCallChainBlock struct {
CallID int64
SubChainID int
Offset int
AuthorUserID int64
Block []byte
CreatedAt int
}
type GroupCallChainBlockPage struct {
Blocks []GroupCallChainBlock
NextOffset int
}

View file

@ -398,6 +398,9 @@ const (
// MessageServiceActionPhoneCall 映射 messageActionPhoneCall:私聊通话
// 结束(含 missed 超时)后落历史的通话条目,sender 恒为主叫。
MessageServiceActionPhoneCall MessageServiceActionKind = "phone_call"
// MessageServiceActionConferenceCall 映射 messageActionConferenceCall:
// ad-hoc conference call 的私聊邀请/状态服务消息。
MessageServiceActionConferenceCall MessageServiceActionKind = "conference_call"
// MessageServiceActionBotAllowed 映射 messageActionBotAllowed:用户授权
// bot 后在 bot 私聊中留下的服务消息。
MessageServiceActionBotAllowed MessageServiceActionKind = "bot_allowed"
@ -424,6 +427,16 @@ type MessagePhoneCallAction struct {
Video bool `json:"video,omitempty"`
}
// MessageConferenceCallAction 是 messageActionConferenceCall 的协议中立载荷。
type MessageConferenceCallAction struct {
CallID int64 `json:"call_id"`
Missed bool `json:"missed,omitempty"`
Active bool `json:"active,omitempty"`
Video bool `json:"video,omitempty"`
Duration int `json:"duration,omitempty"`
OtherParticipants []Peer `json:"other_participants,omitempty"`
}
// MessageBotAllowedAction 是 messageActionBotAllowed 的协议中立载荷。
type MessageBotAllowedAction struct {
AttachMenu bool `json:"attach_menu,omitempty"`
@ -450,14 +463,15 @@ type MessageRequestedPeerAction struct {
// MessageServiceAction 是私聊服务消息动作的协议中立表示。
type MessageServiceAction struct {
Kind MessageServiceActionKind `json:"kind"`
Photo *Photo `json:"photo,omitempty"`
Call *MessagePhoneCallAction `json:"call,omitempty"`
BotAllowed *MessageBotAllowedAction `json:"bot_allowed,omitempty"`
WebViewData *MessageWebViewDataAction `json:"web_view_data,omitempty"`
RequestedPeer *MessageRequestedPeerAction `json:"requested_peer,omitempty"`
ChatThemeEmoticon string `json:"chat_theme_emoticon,omitempty"`
StarGift *MessageStarGiftAction `json:"star_gift,omitempty"`
Kind MessageServiceActionKind `json:"kind"`
Photo *Photo `json:"photo,omitempty"`
Call *MessagePhoneCallAction `json:"call,omitempty"`
ConferenceCall *MessageConferenceCallAction `json:"conference_call,omitempty"`
BotAllowed *MessageBotAllowedAction `json:"bot_allowed,omitempty"`
WebViewData *MessageWebViewDataAction `json:"web_view_data,omitempty"`
RequestedPeer *MessageRequestedPeerAction `json:"requested_peer,omitempty"`
ChatThemeEmoticon string `json:"chat_theme_emoticon,omitempty"`
StarGift *MessageStarGiftAction `json:"star_gift,omitempty"`
}
// MessageStarGiftAction 是 messageActionStarGift 的协议中立载荷:内嵌礼物快照(贴纸/星价)

View file

@ -105,7 +105,10 @@ type PhoneCall struct {
P2PAllowed bool
DiscardReason PhoneCallDiscardReason
Duration int
// DiscardReasonSlug is set for migrate_conference and must resolve through
// phone.getGroupCall(inputGroupCallSlug) before the discarded update is sent.
DiscardReasonSlug string
Duration int
// PrivacyP2P 与 Connections 自 PhoneCallRequest 原样保留(见其注释)。
PrivacyP2P bool