Ctrl K
ComunidadGuías…

internal/domain/groupcall.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package domain

import "errors"

// Group call (supergroup voice chat) domain model. The signaling truth lives in GroupCallStore (memory/postgres
// dual implementation), and the version protocol requires version to be persisted: the client ignores an
// updateGroupCallParticipants with a version lower than its local cache, so a rollback on restart would silently disconnect the whole room.

// GroupCallState is the group call state.
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"
)

// Group call business errors; the rpc layer maps them to 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 is the authoritative state of one group call.
type GroupCall struct {
	ID            int64
	AccessHash    int64
	ChannelID     int64
	CreatorUserID int64
	Kind          GroupCallKind
	State         GroupCallState
	Title         string
	JoinMuted     bool
	// RtmpStream=true means this is an RTMP live room (ingest goes through external RTMP, viewers pull
	// via broadcast); join creates no SFU connection and connection params return stream JSON.
	RtmpStream bool
	// ScheduleDate>0 means the scheduled call has not started (the client shows a countdown panel and does not join);
	// after startScheduledGroupCall clears it, the client automatically initialJoins. Persisted column.
	ScheduleDate int
	// ScheduleStartSubscribed is a per-viewer projection field (not a persisted column): whether the current viewer
	// subscribed to the start reminder. Filled per viewer before RPC/push egress (same kind of semantics as the Creator flag).
	ScheduleStartSubscribed bool
	// Version is the participant protocol version: +1 within the transaction on every participant change, monotonic and persisted.
	Version           int
	ParticipantsCount int
	CreatedAt         int
	DiscardedAt       int
	Duration          int
	// StartedMsgID is the channel message id of messageActionGroupCall(started) (on discard the
	// client uses it to locate the starting service message; currently only recorded).
	StartedMsgID int
	// InviteSlug/InviteLink are used only by conference. The slug must be stably parseable in the migrate reason and
	// InputGroupCallSlug; the link is the full URL used by the client UI for copy/share.
	InviteSlug string
	InviteLink string
	// RandomID is the idempotency key of a conference create (creator_user_id + random_id).
	RandomID int64
	// MigratedFromPhoneCallID records which P2P call it was upgraded from; 0 for a link-only create.
	MigratedFromPhoneCallID int64
}

// Active reports whether the call is still ongoing.
func (c GroupCall) Active() bool {
	return c.State == GroupCallStateActive
}

func (c GroupCall) Conference() bool {
	return c.Kind == GroupCallKindConference
}

// GroupCallParticipant is one participant in the room.
type GroupCallParticipant struct {
	CallID int64
	UserID int64
	// A non-zero JoinAsChannelID means joining as that channel/group identity (anonymous-admin semantics), with TL output
	// participant.peer=PeerChannel; 0 = join as the user's own identity. The identity's unique key is still UserID,
	// so rejoining with a different identity is a replacement.
	JoinAsChannelID int64
	// SSRC is the audio ssrc the client self-reports in the join JSON (uint32 range, stored as int64 to avoid sign pitfalls).
	SSRC       int64
	JoinDate   int
	ActiveDate int
	Muted      bool
	// When MutedByAdmin=true, can_self_unmute=false (admin mute/default-mute policy).
	MutedByAdmin bool
	// VolumeByAdmin is the global volume set by an admin (1..20000); 0=unset.
	VolumeByAdmin int
	// A non-zero RaiseHandRating means the hand is raised; the value increases monotonically for ordering.
	RaiseHandRating int64
	// VideoJSON / PresentationJSON are the raw JSON snapshots of tg.GroupCallParticipantVideo
	// (enabled in M3/M4; in M0/M1 they only transparently store self-edits and are not forwarded).
	VideoJSON        []byte
	PresentationJSON []byte
	// PublicKey/JoinBlock are used only by conference. The server does not parse E2E content; it only persists
	// opaque bytes for chain block backfill/forwarding.
	PublicKey []byte
	JoinBlock []byte
	Left      bool
	// LastCheckDate is the checkGroupCall keepalive watermark. Note: the client sends checkGroupCall
	// only in the Connecting state (the heartbeat stops once media is connected), so a disconnect decision must
	// combine this with SFU media-plane liveness for a double expiry (see sweeper); never rely on this field alone.
	LastCheckDate int
}

// CreateGroupCallRequest creates a group call.
type CreateGroupCallRequest struct {
	ChannelID     int64
	CreatorUserID int64
	RandomID      int64
	Title         string
	Now           int
}

// JoinGroupCallRequest joins/rejoins a group call (rejoin keeps the same key but takes a new ssrc).
type JoinGroupCallRequest struct {
	CallID int64
	UserID int64
	// Non-zero JoinAsChannelID = join as a channel identity (the caller has already checked permissions).
	JoinAsChannelID int64
	SSRC            int64
	Muted           bool
	IsAdmin         bool
	PublicKey       []byte
	JoinBlock       []byte
	// VideoJSON is the video internal state minted by this join (endpoint+source group+active); a rejoin
	// replaces it wholesale and **clears the old PresentationJSON** (after the client's main connection rejoins it re-sends
	// joinGroupCallPresentation, so the old screen registration must be invalidated).
	VideoJSON []byte
	Now       int
}

// GroupCallMutation is the result of one participant-dimension change: the updated call row (with the new version)
// and the affected participant row (for pushing 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 is a field-level update of editGroupCallParticipant (nil=unchanged).
type GroupCallParticipantUpdate struct {
	Muted            *bool
	MutedByAdmin     *bool
	VolumeByAdmin    *int
	RaiseHandRating  *int64
	VideoJSON        *[]byte
	PresentationJSON *[]byte
	Now              int
}

// GroupCallParticipantOverride is a per-viewer view override (setter→target):
// local mute/local volume, visible only to the setter, not entering the room-wide version.
type GroupCallParticipantOverride struct {
	MutedByYou bool
	Volume     int // 0=unset
}

// GroupCallParticipantPage is the paginated result of getGroupParticipants.
type GroupCallParticipantPage struct {
	Count        int
	Participants []GroupCallParticipant
	NextOffset   string
	Version      int
}

// GroupCallInvite is the index of an invite service message a conference call sent in a private chat.
type GroupCallInvite struct {
	CallID        int64
	InviterUserID int64
	InviteeUserID int64
	MessageID     int
	Status        GroupCallInviteStatus
	Video         bool
	CreatedAt     int
	UpdatedAt     int
}

// GroupCallChainBlockLatestOffset is the sentinel value clients use to request the current sub-chain's latest block.
const GroupCallChainBlockLatestOffset = -1

// GroupCallChainBlock is an opaque block of the conference E2E chain.
type GroupCallChainBlock struct {
	CallID       int64
	SubChainID   int
	Offset       int
	AuthorUserID int64
	Block        []byte
	CreatedAt    int
}

type GroupCallChainBlockPage struct {
	Blocks     []GroupCallChainBlock
	NextOffset int
}