import { Conversation, DefaultConversationRoleName, MutedStatus, NewConversation, QualifiedUserClients, RemoteConversations, MLSConversation, SUBCONVERSATION_ID } from '@wireapp/api-client/lib/conversation'; import { BackendEvent, ConversationMemberLeaveEvent } from '@wireapp/api-client/lib/event'; import { QualifiedId } from '@wireapp/api-client/lib/user'; import { XOR } from '@wireapp/commons/lib/util/TypeUtil'; import { APIClient } from '@wireapp/api-client'; import { TypedEventEmitter } from '@wireapp/commons'; import { AddUsersParams, BaseCreateConversationResponse, SendMlsMessageParams, SendResult } from './ConversationService.types'; import { MessageTimer, RemoveUsersParams } from '../../conversation/'; import { MLSService, MLSServiceEvents } from '../../messagingProtocols/mls'; import { ProteusService } from '../../messagingProtocols/proteus'; import { AddUsersToProteusConversationParams, SendProteusMessageParams } from '../../messagingProtocols/proteus/ProteusService/ProteusService.types'; import { HandledEventResult } from '../../notification'; import { CoreDatabase } from '../../storage/CoreDB'; import { SubconversationService } from '../SubconversationService/SubconversationService'; type Events = { MLSConversationRecovered: { conversationId: QualifiedId; }; [MLSServiceEvents.MLS_EVENT_DISTRIBUTED]: { events: any; time: string; }; }; export declare class ConversationService extends TypedEventEmitter { private readonly apiClient; private readonly proteusService; private readonly coreDatabase; private readonly groupIdFromConversationId; private readonly subconversationService; private readonly isMLSConversationRecoveryEnabled; private readonly _mlsService?; readonly messageTimer: MessageTimer; private readonly logger; private groupIdConversationMap; private MLSRecoveryOrchestrator; constructor(apiClient: APIClient, proteusService: ProteusService, coreDatabase: CoreDatabase, groupIdFromConversationId: (conversationId: QualifiedId, subconversationId?: SUBCONVERSATION_ID) => Promise, subconversationService: SubconversationService, isMLSConversationRecoveryEnabled: () => Promise, _mlsService?: MLSService | undefined); get mlsService(): MLSService; /** * Get a fresh list from backend of clients for all the participants of the conversation. * @fixme there are some case where this method is not enough to detect removed devices * @param {string} conversationId * @param {string} conversationDomain? - If given will send the message to the new qualified endpoint */ fetchAllParticipantsClients(conversationId: QualifiedId): Promise; /** * Create a group conversation. * * This method might fail with a `BackendsNotConnectedError` if there are users from not connected backends that are part of the payload * * @note Do not include yourself as the requestor * @see https://staging-nginz-https.zinfra.io/swagger-ui/#!/conversations/createGroupConversation * * @param conversationData Payload object for group creation * @returns Resolves when the conversation was created */ createProteusConversation(conversationData: NewConversation): Promise; getConversation(conversationId: QualifiedId): Promise; getMLSSelfConversation(): Promise; getConversations(conversationIds?: QualifiedId[]): Promise; addUsersToProteusConversation(params: AddUsersToProteusConversationParams): Promise; removeUserFromConversation(conversationId: QualifiedId, userId: QualifiedId): Promise; /** * Sends a message to a conversation * @return resolves with the sending status */ send(params: XOR): Promise; sendTypingStart(conversationId: QualifiedId): Promise; sendTypingStop(conversationId: QualifiedId): Promise; /** * Blacklists a conversation. * When conversations is blacklisted, it means that it will be completely ignored by a client, even though it does exist on backend and we're the conversation member. * @param conversationId id of the conversation to blacklist */ readonly blacklistConversation: (conversationId: QualifiedId) => Promise; /** * Removes a conversation from the blacklists. * @param conversationId id of the conversation to remove from the blacklist */ readonly removeConversationFromBlacklist: (conversationId: QualifiedId) => Promise; /** * returns the number of messages that are in the queue expecting to be sent */ isSendingMessage(): boolean; setConversationMutedStatus(conversationId: QualifiedId, status: MutedStatus, muteTimestamp: number | Date): Promise; toggleArchiveConversation(conversationId: QualifiedId, archived: boolean, archiveTimestamp?: number | Date): Promise; setMemberConversationRole(conversationId: QualifiedId, userId: QualifiedId, conversationRole: DefaultConversationRoleName | string): Promise; /** * ############################################### * ################ MLS Functions ################ * ############################################### */ /** * Will create a conversation on backend and register it to CoreCrypto once created * @param conversationData */ createMLSConversation(conversationData: NewConversation, selfUserId: QualifiedId, selfClientId: string): Promise; /** * Centralized handler for scenarios where an MLS conversation is detected as broken. * It resets the conversation and then invokes the provided callback so callers can retry * their original operation (e.g., re-adding/removing users, re-joining, etc.) with the new group id. * * Contract: * - input: conversationId to reset; callback invoked after reset with the new group id * - output: the value returned by the callback * - error: throws if reset fails or new group id is missing */ private handleBrokenMLSConversation; /** * Will create a conversation on backend and register it to CoreCrypto once created * @param conversationData */ establishMLSGroupConversation(groupId: string, userIdsToAdd: QualifiedId[], selfUserId: QualifiedId, selfClientId: string, conversationQualifiedId: QualifiedId): Promise; /** * Send an MLS message wrapped with recovery. * * Uses the MLS recovery orchestrator to handle transient MLS errors (for example, wrong epoch) * according to per-operation policies. When configured, the original send is retried once * after a successful recovery. Unrecoverable errors are re-thrown by the orchestrator. * The low-level send logic lives in {@link performSendMLSMessageAPI}. */ private sendMLSMessage; private performSendMLSMessageAPI; /** * Add users to an existing MLS group with recovery. * * Claims key packages and passes them to CoreCrypto.addClientsToConversation. The MLS recovery * orchestrator handles recoverable failures (e.g., wrong epoch) and may retry the operation once * depending on policy. The optional shouldRetry flag is ignored; retries are governed by policies. * * @param qualifiedUsers List of qualified user ids (use skipOwnClientId on self to avoid claiming its key package) * @param groupId Id of the MLS group to add users to * @param conversationId Qualified id of the conversation */ addUsersToMLSConversation({ qualifiedUsers, groupId, conversationId, }: Required & { shouldRetry?: boolean; }): Promise; private performAddUsersToMLSConversationAPI; /** * Remove users from an existing MLS group with recovery. * * The MLS recovery orchestrator handles recoverable failures and may retry the operation once * depending on policy. The optional shouldRetry flag is ignored; retries are policy-driven. */ removeUsersFromMLSConversation({ groupId, conversationId, qualifiedUserIds, }: RemoveUsersParams & { shouldRetry?: boolean; }): Promise; private performRemoveUsersFromMLSConversationAPI; /** * Join an MLS conversation via external commit with recovery. * * If the group is not established or is out of date, the orchestrator recovers accordingly. * The join operation itself is not automatically re-run by policy. */ joinByExternalCommit(conversationId: QualifiedId): Promise; private performJoinByExternalCommitAPI; private refreshGroupIdConversationMap; private getConversationByGroupId; /** * React to a key material update failure using the recovery orchestrator. * * The original error is forwarded to the orchestrator under the 'keyMaterialUpdate' operation * so it can map and apply the configured recovery policy. Unrecoverable errors are logged. */ private reactToKeyMaterialUpdateFailure; private resetMLSConversation; /** * Will check if mls group exists locally. * @param groupId groupId of the conversation */ mlsGroupExistsLocally(groupId: string): Promise; /** * Will check if mls group is established locally. * Group is established after the first commit was sent in the group and epoch number is at least 1. * @param groupId groupId of the conversation */ isMLSGroupEstablishedLocally(groupId: string): Promise; wipeMLSConversation: (groupId: string) => Promise; private matchesEpoch; handleConversationsEpochMismatch(): Promise; /** * Handles epoch mismatch in a subconversation. * @param subconversation - subconversation */ private handleSubconversationEpochMismatch; /** * Handles epoch mismatch in a MLS conversation. * @param mlsConversation - mls conversation */ private handleConversationEpochMismatch; /** * Handles epoch mismatch in a MLS group. * Compares the epoch of the local group with the epoch of the remote conversation. * If the epochs do not match, it will call onEpochMismatch callback. * @param groupId - id of the MLS group * @param epoch - epoch of the remote conversation * @param onEpochMismatch - callback to be called when epochs do not match */ private hasEpochMismatch; /** * Get a MLS 1:1-conversation with a given user. * @param userId - qualified user id */ getMLS1to1Conversation(userId: QualifiedId): Promise; /** * Will try registering mls 1:1 conversation adding the other user. * If it fails and the conversation is already established, it will try joining via external commit instead. * * @param mlsConversation - mls 1:1 conversation * @param selfUser - user and client ids of the self user * @param otherUserId - id of the other user */ readonly establishMLS1to1Conversation: (groupId: string, selfUser: { user: QualifiedId; client: string; }, otherUserId: QualifiedId, shouldRetry?: boolean) => Promise; /** * Will try to register mls group by sending an empty commit to establish it. * After group was successfully established, it will try to add other users to the group. * * @param groupId - id of the MLS group * @param conversationId - id of the conversation * @param selfUserId - id of the self user * @param qualifiedUsers - list of qualified users to add to the group (should not include the self user) */ tryEstablishingMLSGroup({ groupId, conversationId, selfUserId, qualifiedUsers, }: { groupId: string; conversationId: QualifiedId; selfUserId: QualifiedId; qualifiedUsers: QualifiedId[]; }): Promise; /** * Handle an inbound MLS message-add event with recovery. * * Policies (see MlsRecoveryOrchestrator): * - WrongEpoch.handleMessageAdd → recover from epoch mismatch and re-run once. * - GroupOutOfSync.handleMessageAdd → not handled here; the error bubbles. * * Returns the decrypted payload when available. Unknown or unrecoverable errors are logged * and result in null so event processing can continue safely. */ private handleMLSMessageAddEvent; private recoverMLSGroupFromEpochMismatch; /** * Handle an MLS welcome event with recovery. * * Policies (see MlsRecoveryOrchestrator): * - OrphanWelcome → join via external commit (no auto re-run). * - ConversationAlreadyExists → wipe local state and re-run welcome once. * * Always resolves to null; the effects are applied to local state. */ private handleMLSWelcomeMessageEvent; private handleOtrMessageAddEvent; private isConversationBlacklisted; /** * Will process one conversation event * @param event The backend event to process * @return Event handling status (if handled successfully also the decrypted payload and the raw event) */ handleEvent(event: BackendEvent): Promise; } export {}; //# sourceMappingURL=ConversationService.d.ts.map