From 9ba070c77fd0734880e58933cc65f601c42b5d05 Mon Sep 17 00:00:00 2001 From: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com> Date: Thu, 30 May 2024 17:44:06 -0700 Subject: [PATCH] Send profile keys in call links --- ts/jobs/conversationJobQueue.ts | 2 + ts/jobs/helpers/sendProfileKey.ts | 2 +- ts/services/calling.ts | 73 ++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/ts/jobs/conversationJobQueue.ts b/ts/jobs/conversationJobQueue.ts index 73b44bdac..fba565bd9 100644 --- a/ts/jobs/conversationJobQueue.ts +++ b/ts/jobs/conversationJobQueue.ts @@ -168,6 +168,8 @@ const profileKeyJobDataSchema = z.object({ conversationId: z.string(), // Note: we will use whichever recipients list is up to date when this job runs revision: z.number().optional(), + // Setting this to true lets you send profile key without adding to contacts + isOneTimeSend: z.boolean().optional(), }); export type ProfileKeyJobData = z.infer; diff --git a/ts/jobs/helpers/sendProfileKey.ts b/ts/jobs/helpers/sendProfileKey.ts index 4f5a80d57..d828746b6 100644 --- a/ts/jobs/helpers/sendProfileKey.ts +++ b/ts/jobs/helpers/sendProfileKey.ts @@ -78,7 +78,7 @@ export async function sendProfileKey( return; } - if (!conversation.get('profileSharing')) { + if (!data?.isOneTimeSend && !conversation.get('profileSharing')) { log.info('No longer sharing profile. Cancelling job.'); return; } diff --git a/ts/services/calling.ts b/ts/services/calling.ts index 74d0f6969..f718b45d5 100644 --- a/ts/services/calling.ts +++ b/ts/services/calling.ts @@ -137,7 +137,10 @@ import { toAdminKeyBytes, } from '../util/callLinks'; import { isAdhocCallingEnabled } from '../util/isAdhocCallingEnabled'; -import { conversationJobQueue } from '../jobs/conversationJobQueue'; +import { + conversationJobQueue, + conversationQueueJobEnum, +} from '../jobs/conversationJobQueue'; import type { ReadCallLinkState } from '../types/CallLink'; const { @@ -351,6 +354,10 @@ export class CallingClass { private callDebugNumber: number = 0; + // Send our profile key to other participants in call link calls to ensure they + // can see our profile info. Only send once per aci until the next app start. + private sendProfileKeysForAdhocCallCache: Set; + constructor() { this.videoCapturer = new GumVideoCapturer({ maxWidth: REQUESTED_VIDEO_WIDTH, @@ -360,6 +367,7 @@ export class CallingClass { this.videoRenderer = new CanvasVideoRenderer(); this.callsLookup = {}; + this.sendProfileKeysForAdhocCallCache = new Set(); } initialize(reduxInterface: CallingReduxInterface, sfuUrl: string): void { @@ -1118,6 +1126,7 @@ export class CallingClass { peerId: conversationId, eraId, callMode, + peekInfo, }) ); } @@ -1136,6 +1145,14 @@ export class CallingClass { ); this.syncGroupCallToRedux(conversationId, groupCall, callMode); + if (callMode === CallMode.Adhoc) { + drop( + this.sendProfileKeysForAdhocCall({ + roomId: conversationId, + peekInfo, + }) + ); + } }, onAudioLevels: groupCall => { const remoteDeviceStates = groupCall.getRemoteDeviceStates(); @@ -1198,6 +1215,7 @@ export class CallingClass { peerId: conversationId, eraId, callMode, + peekInfo, }) ); } @@ -1242,18 +1260,71 @@ export class CallingClass { callMode, peerId, eraId, + peekInfo, }: { callMode: CallMode.Group | CallMode.Adhoc; peerId: string; eraId: string; + peekInfo: PeekInfo | null; }): Promise { if (callMode === CallMode.Group) { drop(this.sendGroupCallUpdateMessage(peerId, eraId)); } else if (callMode === CallMode.Adhoc) { this.reduxInterface?.joinedAdhocCall(peerId); + drop(this.sendProfileKeysForAdhocCall({ roomId: peerId, peekInfo })); } } + private async sendProfileKeysForAdhocCall({ + roomId, + peekInfo, + }: { + roomId: string; + peekInfo: PeekInfo | null | undefined; + }): Promise { + if (!peekInfo) { + return; + } + + const ourAci = window.textsecure.storage.user.getCheckedAci(); + const reason = `sendProfileKeysForAdhocCall(${roomId})`; + peekInfo.devices.forEach(async device => { + const aci = device.userId ? this.formatUserId(device.userId) : null; + if ( + !aci || + aci === ourAci || + this.sendProfileKeysForAdhocCallCache.has(aci) + ) { + return; + } + + const logId = `sendProfileKeysForAdhocCall aci=${aci}`; + const conversation = window.ConversationController.lookupOrCreate({ + serviceId: aci, + reason, + }); + if (!conversation) { + log.warn(`${logId}: Could not lookup or create conversation for aci`); + return; + } + + if (conversation.isBlocked()) { + log.info(`${logId}: Skipping blocked aci`); + return; + } + + log.info(`${logId}: Sending profile key`); + drop( + conversationJobQueue.add({ + type: conversationQueueJobEnum.enum.ProfileKey, + conversationId: conversation.id, + isOneTimeSend: true, + }) + ); + this.sendProfileKeysForAdhocCallCache.add(aci); + }); + } + public async joinCallLinkCall({ roomId, rootKey,