conversations

package
v1.1.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 21, 2026 License: AGPL-3.0 Imports: 16 Imported by: 0

Documentation

Overview

Package conversations provides the core Conversations Service for the Lesser project's API alignment. This service handles direct message conversations including creation, participant management, read status tracking, and real-time event emission. It supports federation for remote participants and maintains privacy by ensuring only participants can access conversation content.

Index

Constants

View Source
const (
	// VisibilityDirect represents direct message visibility
	VisibilityDirect = "direct"

	// ConversationMessageEvent is emitted when a message is sent to a conversation
	ConversationMessageEvent = "conversation.message"

	// ConversationReadEvent is emitted when a conversation is marked as read
	ConversationReadEvent = "conversation.read"

	// ConversationUpdatedEvent is emitted when conversation metadata is updated
	ConversationUpdatedEvent = "conversation.updated"
)

Variables

View Source
var (
	// ErrConversationValidationFailed is returned when conversation validation fails
	ErrConversationValidationFailed = apperrors.ValidationFailedWithField("conversation")

	// ErrGetSenderAccount is returned when getting sender account fails
	ErrGetSenderAccount = apperrors.FailedToGet("sender account", errors.New("failed to get sender account"))

	// ErrInvalidRecipient is returned when recipient validation fails
	ErrInvalidRecipient = apperrors.NewValidationError("recipient", "invalid")

	// ErrLookupExistingConversation is returned when looking up existing conversation fails
	ErrLookupExistingConversation = apperrors.FailedToQuery("existing conversation", errors.New("failed to lookup existing conversation"))

	// ErrCreateConversation is returned when conversation creation fails
	ErrCreateConversation = apperrors.FailedToCreate("conversation", errors.New("failed to create conversation"))

	// ErrCreateDirectMessage is returned when direct message creation fails
	ErrCreateDirectMessage = apperrors.FailedToCreate("direct message", errors.New("failed to create direct message"))

	// ErrGetConversation is returned when conversation retrieval fails
	ErrGetConversation = apperrors.FailedToGet("conversation", errors.New("failed to get conversation"))

	// ErrNotConversationParticipant is returned when user is not a participant in conversation
	ErrNotConversationParticipant = apperrors.AccessDeniedForResource("conversation", "participant")

	// ErrMarkConversationRead is returned when marking conversation as read fails
	ErrMarkConversationRead = apperrors.FailedToUpdate("conversation read status", errors.New("failed to mark conversation as read"))

	// ErrGetUserConversations is returned when getting user conversations fails
	ErrGetUserConversations = apperrors.FailedToList("user conversations", errors.New("failed to get user conversations"))

	// ErrGetConversationMessages is returned when getting conversation messages fails
	ErrGetConversationMessages = apperrors.FailedToList("conversation messages", errors.New("failed to get conversation messages"))

	// ErrRecipientsRequired is returned when recipients list is required but empty
	ErrRecipientsRequired = apperrors.NewValidationError("recipients", "required")

	// ErrDirectMessageRequiresSingleRecipient is returned when the DM command contains multiple recipients (group chats not supported in v1).
	ErrDirectMessageRequiresSingleRecipient = apperrors.NewValidationError("recipients", "must contain exactly 1 recipient")

	// ErrConversationMustBeOneToOne is returned when a conversation has an unexpected participant count.
	ErrConversationMustBeOneToOne = apperrors.NewValidationError("conversation", "must be 1:1")

	// ErrContentTooLongConversation is returned when conversation content is too long
	ErrContentTooLongConversation = apperrors.NewValidationError("content", "too long (max 5000 characters)")

	// ErrInvalidInReplyToIDConversation is returned when in_reply_to_id is invalid for conversation
	ErrInvalidInReplyToIDConversation = apperrors.NewValidationError("in_reply_to_id", "invalid")

	// ErrCanOnlyReplyToDirectMessages is returned when attempting to reply to non-direct message
	ErrCanOnlyReplyToDirectMessages = apperrors.NewValidationError("reply_target", "can only reply to direct messages")

	// ErrConversationNotFound is returned when conversation is not found
	ErrConversationNotFound = apperrors.NewAppError(apperrors.CodeNotFound, apperrors.CategoryBusiness, "conversation not found")

	// ErrGetAccount is returned when account retrieval fails
	ErrGetAccount = apperrors.FailedToGet("account", errors.New("failed to get account"))

	// ErrDeleteConversation is returned when conversation deletion fails
	ErrDeleteConversation = apperrors.FailedToDelete("conversation", errors.New("failed to delete conversation"))

	// ErrDeleteMessage is returned when message deletion (delete-for-me tombstone) fails.
	ErrDeleteMessage = apperrors.FailedToDelete("direct message", errors.New("failed to delete message"))

	// ErrDirectMessageBlocked is returned when a DM cannot be sent due to a block.
	//
	// Note: intentionally generic to avoid leaking block direction.
	ErrDirectMessageBlocked = apperrors.AccessDeniedForResource("direct message", "blocked")

	// ErrMessageRequestPending is returned when a sender attempts to send additional messages
	// before a message request has been accepted.
	ErrMessageRequestPending = apperrors.NewAppError(apperrors.CodeForbidden, apperrors.CategoryBusiness, "Message request pending")

	// ErrMessageRequestMediaNotAllowed is returned when a DM request includes attachments.
	ErrMessageRequestMediaNotAllowed = apperrors.NewValidationError("media", "not allowed in message requests")
)

Conversation service errors

Functions

This section is empty.

Types

type AcceptMessageRequestCommand added in v1.1.14

type AcceptMessageRequestCommand struct {
	ConversationID string `json:"conversation_id" validate:"required"`
	UserID         string `json:"user_id" validate:"required"`
}

AcceptMessageRequestCommand contains data needed to accept a pending message request.

type ConversationFolder added in v1.1.14

type ConversationFolder string

ConversationFolder indicates whether a thread appears in the viewer's inbox or requests list.

const (
	// ConversationFolderInbox is the user's accepted DM inbox.
	ConversationFolderInbox ConversationFolder = "INBOX"
	// ConversationFolderRequests is the user's pending message requests folder.
	ConversationFolderRequests ConversationFolder = "REQUESTS"
)

type ConversationResult

type ConversationResult struct {
	Conversation *models.Conversation `json:"conversation"`
	Events       []*streaming.Event   `json:"events"`
}

ConversationResult contains a conversation and associated events

type ConversationWithMessages

type ConversationWithMessages struct {
	Conversation *models.Conversation                        `json:"conversation"`
	Messages     *interfaces.PaginatedResult[*models.Status] `json:"messages"`
	Events       []*streaming.Event                          `json:"events"`
}

ConversationWithMessages contains a conversation and its message history

type CreateConversationCommand added in v1.1.14

type CreateConversationCommand struct {
	CreatorID     string `json:"creator_id" validate:"required"`
	ParticipantID string `json:"participant_id" validate:"required"`
}

CreateConversationCommand contains data needed to create a 1:1 conversation.

type DeclineMessageRequestCommand added in v1.1.14

type DeclineMessageRequestCommand struct {
	ConversationID string `json:"conversation_id" validate:"required"`
	UserID         string `json:"user_id" validate:"required"`
}

DeclineMessageRequestCommand contains data needed to decline a pending message request.

type DeleteConversationCommand

type DeleteConversationCommand struct {
	ConversationID string `json:"conversation_id" validate:"required"`
	UserID         string `json:"user_id" validate:"required"`
}

DeleteConversationCommand contains data needed to delete a conversation

type DeleteMessageCommand added in v1.1.14

type DeleteMessageCommand struct {
	MessageID string `json:"message_id" validate:"required"`
	UserID    string `json:"user_id" validate:"required"`
}

DeleteMessageCommand contains data needed to delete a direct message for the viewer.

type FederationService

type FederationService interface {
	QueueActivity(ctx context.Context, activity *activitypub.Activity) error
}

FederationService defines the interface for federation operations

type GetConversationQuery

type GetConversationQuery struct {
	ConversationID string                       `json:"conversation_id" validate:"required"`
	ViewerID       string                       `json:"viewer_id" validate:"required"` // Must be a participant
	Pagination     interfaces.PaginationOptions `json:"pagination"`
}

GetConversationQuery contains parameters for retrieving a conversation

type ListConversationsQuery

type ListConversationsQuery struct {
	UserID     string                       `json:"user_id" validate:"required"`
	Pagination interfaces.PaginationOptions `json:"pagination"`
	Folder     ConversationFolder           `json:"folder"`
	OnlyUnread bool                         `json:"only_unread"`
}

ListConversationsQuery contains parameters for listing user conversations

type MarkConversationReadCommand

type MarkConversationReadCommand struct {
	ConversationID string `json:"conversation_id" validate:"required"`
	UserID         string `json:"user_id" validate:"required"`
}

MarkConversationReadCommand contains data needed to mark a conversation as read

type MessageResult

type MessageResult struct {
	Message      *models.Status       `json:"message"`
	Conversation *models.Conversation `json:"conversation"`
	Events       []*streaming.Event   `json:"events"`
}

MessageResult contains a direct message and associated events

type Result

type Result struct {
	Conversations *interfaces.PaginatedResult[*models.Conversation] `json:"conversations"`
	Events        []*streaming.Event                                `json:"events"`
}

Result contains multiple conversations with pagination

type SendDirectMessageCommand

type SendDirectMessageCommand struct {
	SenderID    string   `json:"sender_id" validate:"required"`
	Recipients  []string `json:"recipients" validate:"required,min=1"`
	Content     string   `json:"content" validate:"required,max=5000"`
	Sensitive   bool     `json:"sensitive"`
	Language    string   `json:"language"`
	MediaIDs    []string `json:"media_ids"`
	InReplyToID string   `json:"in_reply_to_id"` // Can reply to messages in the conversation
}

SendDirectMessageCommand contains all data needed to send a direct message

type SendMessageCommand added in v1.1.14

type SendMessageCommand struct {
	SenderID       string   `json:"sender_id" validate:"required"`
	ConversationID string   `json:"conversation_id" validate:"required"`
	Content        string   `json:"content" validate:"required,max=5000"`
	Sensitive      bool     `json:"sensitive"`
	Language       string   `json:"language"`
	MediaIDs       []string `json:"media_ids"`
	InReplyToID    string   `json:"in_reply_to_id"`
}

SendMessageCommand contains all data needed to send a message to an existing 1:1 conversation.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service provides conversation operations for direct messages

func NewService

func NewService(
	conversationRepo interfaces.ConversationRepository,
	noteRepo interfaces.StatusRepository,
	dmTombstoneRepo directMessageTombstoneRepository,
	accountRepo interfaces.AccountRepository,
	relationshipRepo interfaces.ConcreteRelationshipRepository,
	userRepo interfaces.UserRepository,
	rateLimitRepo interfaces.RateLimitRepository,
	auditRepo interfaces.AuditRepository,
	publisher streaming.Publisher,
	federation FederationService,
	logger *zap.Logger,
	domainName string,
) *Service

NewService creates a new Conversations Service with the required dependencies

func (*Service) AcceptMessageRequest added in v1.1.14

func (s *Service) AcceptMessageRequest(ctx context.Context, cmd *AcceptMessageRequestCommand) (*ConversationResult, error)

AcceptMessageRequest moves a pending request thread into the user's inbox.

func (*Service) CreateConversation added in v1.1.14

func (s *Service) CreateConversation(ctx context.Context, cmd *CreateConversationCommand) (*ConversationResult, error)

CreateConversation creates (or returns) a 1:1 conversation between the caller and another participant.

func (*Service) DeclineMessageRequest added in v1.1.14

func (s *Service) DeclineMessageRequest(ctx context.Context, cmd *DeclineMessageRequestCommand) (*ConversationResult, error)

DeclineMessageRequest hides a request thread from the recipient by setting requestState=DECLINED.

func (*Service) DeleteConversation

func (s *Service) DeleteConversation(ctx context.Context, cmd *DeleteConversationCommand) (*ConversationResult, error)

DeleteConversation implements delete-for-me semantics for a DM conversation. It marks the viewer's participant record DeletedAt without deleting shared conversation data.

func (*Service) DeleteMessage added in v1.1.14

func (s *Service) DeleteMessage(ctx context.Context, cmd *DeleteMessageCommand) (bool, error)

DeleteMessage implements delete-for-me semantics for a direct message (Status). It creates a per-viewer tombstone keyed by (viewerUsername, statusID).

func (*Service) GetConversation

func (s *Service) GetConversation(ctx context.Context, query *GetConversationQuery) (*ConversationWithMessages, error)

GetConversation retrieves a conversation with its message history

func (*Service) GetConversationLastStatus added in v1.1.14

func (s *Service) GetConversationLastStatus(ctx context.Context, conversationID, viewerID string) (*models.Status, error)

GetConversationLastStatus returns the latest direct message in the conversation that is visible to the viewer and is not deleted-for-viewer.

func (*Service) ListConversations

func (s *Service) ListConversations(ctx context.Context, query *ListConversationsQuery) (*Result, error)

ListConversations retrieves conversations for a user with pagination

func (*Service) MarkConversationRead

func (s *Service) MarkConversationRead(ctx context.Context, cmd *MarkConversationReadCommand) (*ConversationResult, error)

MarkConversationRead marks a conversation as read for a specific user

func (*Service) SendDirectMessage

func (s *Service) SendDirectMessage(ctx context.Context, cmd *SendDirectMessageCommand) (*MessageResult, error)

SendDirectMessage creates or updates a conversation, creates a direct message, and emits events

func (*Service) SendMessage added in v1.1.14

func (s *Service) SendMessage(ctx context.Context, cmd *SendMessageCommand) (*MessageResult, error)

SendMessage sends a message in an existing 1:1 conversation, enforcing strict participant authz.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL