From 9395f6590a641fdf66f77f4f32eb98a937e42c4a Mon Sep 17 00:00:00 2001 From: Pierre Lehnen Date: Thu, 4 Dec 2025 14:31:29 -0300 Subject: [PATCH] chore: create call history entries for external voice calls --- .../server/services/media-call/service.ts | 51 ++++++++++++++++++- packages/core-typings/src/ICallHistoryItem.ts | 9 ++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/apps/meteor/server/services/media-call/service.ts b/apps/meteor/server/services/media-call/service.ts index fff0a13465d2e..3950721d1e84b 100644 --- a/apps/meteor/server/services/media-call/service.ts +++ b/apps/meteor/server/services/media-call/service.ts @@ -1,5 +1,12 @@ import { api, ServiceClassInternal, type IMediaCallService, Authorization } from '@rocket.chat/core-services'; -import type { IMediaCall, IUser, IRoom, IInternalMediaCallHistoryItem, CallHistoryItemState } from '@rocket.chat/core-typings'; +import type { + IMediaCall, + IUser, + IRoom, + IInternalMediaCallHistoryItem, + CallHistoryItemState, + IExternalMediaCallHistoryItem, +} from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; import { callServer, type IMediaCallServerSettings } from '@rocket.chat/media-calls'; import { isClientMediaSignal, type ClientMediaSignal, type ServerMediaSignal } from '@rocket.chat/media-signaling'; @@ -81,12 +88,52 @@ export class MediaCallService extends ServiceClassInternal implements IMediaCall } if (call.uids.length !== 2) { - return; + return this.saveExternalCallToHistory(call); } return this.saveInternalCallToHistory(call); } + private async saveExternalCallToHistory(call: IMediaCall): Promise { + const callerIsInternal = call.caller.type === 'user'; + const calleeIsInternal = call.callee.type === 'user'; + + if (callerIsInternal && calleeIsInternal) { + logger.warn({ msg: 'Attempt to save an external call history with a call that is not external', callId: call._id }); + return; + } + + if (!callerIsInternal && !calleeIsInternal) { + logger.warn({ msg: 'Attempt to save an external call history with an invalid call', callId: call._id }); + return; + } + + const state = this.getCallHistoryItemState(call); + const duration = this.getCallDuration(call); + const direction = callerIsInternal ? 'outbound' : 'inbound'; + const uid = callerIsInternal ? call.caller.id : call.callee.id; + const contact = callerIsInternal ? call.callee : call.caller; + + const contactExtension = contact.sipExtension || contact.id; + + const historyItem: InsertionModel = { + uid, + ts: call.createdAt, + callId: call._id, + state, + type: 'media-call', + duration, + endedAt: call.endedAt || new Date(), + external: true, + direction, + contactExtension, + }; + + await CallHistory.insertOne(historyItem).catch((error: unknown) => + logger.error({ msg: 'Failed to insert item into Call History', error }), + ); + } + private async saveInternalCallToHistory(call: IMediaCall): Promise { if (call.caller.type !== 'user' || call.callee.type !== 'user') { logger.warn({ msg: 'Attempt to save an internal call history with a call that is not internal', callId: call._id }); diff --git a/packages/core-typings/src/ICallHistoryItem.ts b/packages/core-typings/src/ICallHistoryItem.ts index 1c0c51ac26620..4ef572eb0b844 100644 --- a/packages/core-typings/src/ICallHistoryItem.ts +++ b/packages/core-typings/src/ICallHistoryItem.ts @@ -42,7 +42,10 @@ export interface IInternalMediaCallHistoryItem extends IMediaCallHistoryItem { messageId?: IMessage['_id']; // Id of the message that was sent after the call ended } -// TODO: IExternalMediaCallHistoryItem, planned for 8.0 -// TODO: IVideoConfHistoryItem, expected in the future but not yet on the roadmap +export interface IExternalMediaCallHistoryItem extends IMediaCallHistoryItem { + external: true; -export type CallHistoryItem = IInternalMediaCallHistoryItem; + contactExtension: string; +} + +export type CallHistoryItem = IInternalMediaCallHistoryItem | IExternalMediaCallHistoryItem;