238 lines
8.4 KiB
Go
238 lines
8.4 KiB
Go
package domain
|
||
|
||
import "errors"
|
||
|
||
// 群通话(超级群语音聊天)领域模型。信令真值在 GroupCallStore(memory/postgres
|
||
// 双实现),版本协议要求 version 必须持久化:客户端忽略 version 小于本地缓存的
|
||
// updateGroupCallParticipants,重启回卷会让整个房间静默失联。
|
||
|
||
// GroupCallState 是群通话状态。
|
||
type GroupCallState string
|
||
|
||
const (
|
||
GroupCallStateActive GroupCallState = "active"
|
||
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")
|
||
ErrGroupCallDiscarded = errors.New("group call already discarded")
|
||
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 是一场群通话的权威态。
|
||
type GroupCall struct {
|
||
ID int64
|
||
AccessHash int64
|
||
ChannelID int64
|
||
CreatorUserID int64
|
||
Kind GroupCallKind
|
||
State GroupCallState
|
||
Title string
|
||
JoinMuted bool
|
||
// RtmpStream=true 表示这是 RTMP 直播房间(推流走外部 RTMP ingest,观众经
|
||
// broadcast 拉流),join 不建 SFU 连接、connection params 返回 stream JSON。
|
||
RtmpStream bool
|
||
// ScheduleDate>0 表示定时通话尚未开始(客户端显示倒计时面板、不入会);
|
||
// startScheduledGroupCall 清零后客户端自动 initialJoin。持久列。
|
||
ScheduleDate int
|
||
// ScheduleStartSubscribed 是 per-viewer 投影字段(非持久列):当前 viewer 是否
|
||
// 订阅了开播提醒。RPC/推送出站前按 viewer 回填(与 Creator flag 同类语义)。
|
||
ScheduleStartSubscribed bool
|
||
// Version 是参与者协议版本:所有参与者变更事务内 +1,单调且持久。
|
||
Version int
|
||
ParticipantsCount int
|
||
CreatedAt int
|
||
DiscardedAt int
|
||
Duration int
|
||
// 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 报告通话是否仍在进行。
|
||
func (c GroupCall) Active() bool {
|
||
return c.State == GroupCallStateActive
|
||
}
|
||
|
||
func (c GroupCall) Conference() bool {
|
||
return c.Kind == GroupCallKindConference
|
||
}
|
||
|
||
// GroupCallParticipant 是房间内一名参与者。
|
||
type GroupCallParticipant struct {
|
||
CallID int64
|
||
UserID int64
|
||
// JoinAsChannelID 非零表示以该频道/群身份入会(匿名管理员语义),TL 输出
|
||
// participant.peer=PeerChannel;0=以本人用户身份。身份唯一键仍是 UserID,
|
||
// 换身份 rejoin 是替换。
|
||
JoinAsChannelID int64
|
||
// SSRC 是客户端在 join JSON 里自报的 audio ssrc(uint32 值域,存 int64 防符号坑)。
|
||
SSRC int64
|
||
JoinDate int
|
||
ActiveDate int
|
||
Muted bool
|
||
// MutedByAdmin=true 时 can_self_unmute=false(管理员禁言/默认静音策略)。
|
||
MutedByAdmin bool
|
||
// VolumeByAdmin 是管理员设定的全局音量(1..20000),0=未设。
|
||
VolumeByAdmin int
|
||
// RaiseHandRating 非零表示举手中,值单调递增用于排序。
|
||
RaiseHandRating int64
|
||
// VideoJSON / PresentationJSON 是 tg.GroupCallParticipantVideo 的原始 JSON
|
||
// 快照(M3/M4 启用;M0/M1 仅透明保存 self-edit,不转发)。
|
||
VideoJSON []byte
|
||
PresentationJSON []byte
|
||
// PublicKey/JoinBlock 仅 conference 使用。服务端不解析 E2E 内容,只持久化
|
||
// opaque bytes 并供 chain block 补拉/转发。
|
||
PublicKey []byte
|
||
JoinBlock []byte
|
||
Left bool
|
||
// LastCheckDate 是 checkGroupCall 保活水位。注意:客户端只在 Connecting 态
|
||
// 发 checkGroupCall(媒体连通后心跳停止),掉线判定必须与 SFU 媒体面活性
|
||
// 取双过期(见 sweeper),绝不能单凭此字段。
|
||
LastCheckDate int
|
||
}
|
||
|
||
// CreateGroupCallRequest 创建群通话。
|
||
type CreateGroupCallRequest struct {
|
||
ChannelID int64
|
||
CreatorUserID int64
|
||
RandomID int64
|
||
Title string
|
||
Now int
|
||
}
|
||
|
||
// JoinGroupCallRequest 加入/重进群通话(rejoin 同主键换新 ssrc)。
|
||
type JoinGroupCallRequest struct {
|
||
CallID int64
|
||
UserID int64
|
||
// JoinAsChannelID 非零=以频道身份入会(调用方已完成权限校验)。
|
||
JoinAsChannelID int64
|
||
SSRC int64
|
||
Muted bool
|
||
IsAdmin bool
|
||
PublicKey []byte
|
||
JoinBlock []byte
|
||
// VideoJSON 是本次 join 铸造的视频内部状态(endpoint+源组+active);rejoin
|
||
// 整体替换并**清空旧 PresentationJSON**(客户端主连接 rejoin 后会重发
|
||
// joinGroupCallPresentation,旧屏幕登记必须作废)。
|
||
VideoJSON []byte
|
||
Now int
|
||
}
|
||
|
||
// GroupCallMutation 是一次参与者维度变更的结果:变更后的 call 行(含新 version)
|
||
// 与受影响的参与者行(推送 updateGroupCallParticipants 用)。
|
||
type GroupCallMutation struct {
|
||
Call GroupCall
|
||
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
|
||
MutedByAdmin *bool
|
||
VolumeByAdmin *int
|
||
RaiseHandRating *int64
|
||
VideoJSON *[]byte
|
||
PresentationJSON *[]byte
|
||
Now int
|
||
}
|
||
|
||
// GroupCallParticipantOverride 是 per-viewer 视图覆盖(setter→target):
|
||
// 本地静音/本地音量,仅 setter 自己可见,不进全房间 version。
|
||
type GroupCallParticipantOverride struct {
|
||
MutedByYou bool
|
||
Volume int // 0=未设
|
||
}
|
||
|
||
// GroupCallParticipantPage 是 getGroupParticipants 的分页结果。
|
||
type GroupCallParticipantPage struct {
|
||
Count int
|
||
Participants []GroupCallParticipant
|
||
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
|
||
}
|