Skip to content

feat: send push notifications for voice calls#39386

Open
pierre-lehnen-rc wants to merge 19 commits intodevelopfrom
feat/voip-send-push
Open

feat: send push notifications for voice calls#39386
pierre-lehnen-rc wants to merge 19 commits intodevelopfrom
feat/voip-send-push

Conversation

@pierre-lehnen-rc
Copy link
Contributor

@pierre-lehnen-rc pierre-lehnen-rc commented Mar 5, 2026

Proposed changes (including videos or screenshots)

Issue(s)

VMUX-47

Steps to test or reproduce

Further comments

Summary by CodeRabbit

  • New Features

    • VoIP push notifications for media calls with distinct notification types and a public API to request/send call push notifications.
    • Automatic push sending on call events (new, answer, end).
  • Improvements

    • Better VoIP token support and ability to skip specific tokens when sending.
    • Conditional notification fields (title/body/from), adjusted delivery/expiry for VoIP pushes, gateway retry limits, and improved media-call logging.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Mar 5, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is targeting the wrong base branch. It should target 8.4.0, but it targets 8.3.0

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Mar 5, 2026

⚠️ No Changeset found

Latest commit: a976955

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds VoIP push support and wiring: voipToken and pushTokenTypes added to typings/models, push sending updated to support VoIP tokens and retry caps, token removal switched to string-based APIs, and media-call components emit and handle VoIP push notifications.

Changes

Cohort / File(s) Summary
API & Token Typings
apps/meteor/app/api/server/v1/push.ts, packages/core-typings/src/IPushToken.ts, packages/core-typings/src/index.ts
Use exported pushTokenTypes in API schema; export full IPushToken; add pushTokenTypes and voipToken.
Push Notification Config / Definitions
packages/core-typings/src/IPushNotificationConfig.ts, apps/meteor/app/push/server/definition.ts
Make from, title, text optional; add useVoipToken?: boolean and skipTokenId?: IPushToken['_id']; remove apn.topicSuffix.
APN & FCM send logic
apps/meteor/app/push/server/apn.ts, apps/meteor/app/push/server/fcm.ts
Change _removeToken callbacks to accept token string; conditionally assign category/body/title/from; APN supports VoIP pushType with 60s expiry when useVoipToken.
Push Service Core
apps/meteor/app/push/server/push.ts
Introduce PUSH_GATEWAY_MAX_RETRIES; add retryOptions (tries, maxRetries); make gateway notification fields optional; propagate useVoipToken and skipTokenId; token lookup uses findAllTokensByUserId or findTokensByUserIdExceptId; remove _replaceToken flow.
Push Token Model & DB
packages/model-typings/src/models/IPushTokenModel.ts, packages/models/src/models/PushToken.ts
Add findAllTokensByUserId and findTokensByUserIdExceptId returning cursors; include voipToken in refreshTokenById; add removeOrUnsetByTokenString.
Media-call push sender & utilities
apps/meteor/server/services/media-call/logger.ts, apps/meteor/server/services/media-call/push/getPushNotificationType.ts, apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
Add scoped logger; map call state → VoIP push type; implement sendVoipPushNotification that builds payload, selects tokens (honors skipTokenId), and calls Push.send with useVoipToken for incoming calls.
Media-call integration & API surface
apps/meteor/server/services/media-call/service.ts, ee/packages/media-calls/src/server/MediaCallServer.ts, ee/packages/media-calls/src/definition/IMediaCallServer.ts
Wire pushNotificationRequest event and sendPushNotification API; emit/listen for push notifications from media-call server.
Media-call agents & call creation
ee/packages/media-calls/src/internal/agents/UserActorAgent.ts, ee/packages/media-calls/src/server/CallDirector.ts
Emit push events on call create/accept/end for callee role; generate call _id with randomUUID() in call creation.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant MediaCallServer
    participant PushService
    participant PushTokenDB
    participant Gateway

    Caller->>MediaCallServer: create/accept/end call (callId)
    MediaCallServer->>MediaCallServer: emit pushNotificationRequest(callId, event)
    MediaCallServer->>PushService: sendPushNotification(callId, event)
    PushService->>PushTokenDB: findAllTokensByUserId / findTokensByUserIdExceptId(userId, skipTokenId)
    PushTokenDB-->>PushService: tokens (may include voipToken)
    PushService->>PushService: set retryOptions (tries/maxRetries = useVoipToken ? 1 : MAX)
    alt useVoipToken & voipToken present
        PushService->>Gateway: send APN VoIP (expiry 60s, pushType='voip')
    else standard token
        PushService->>Gateway: send APN/FCM with retries/backoff
    end
    Gateway-->>PushService: delivery result / error
    PushService->>PushTokenDB: removeOrUnsetByTokenString(token) on invalid token
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • KevLehman
  • tassoevan
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: send push notifications for voice calls' clearly and concisely describes the main objective of this changeset: implementing VoIP push notification functionality.
Linked Issues check ✅ Passed The linked issue VMUX-47 requires sending push notifications when calls are initiated. The PR implements sendVoipPushNotification across multiple modules, integrates it with call events, and supports various notification types for different call states.
Out of Scope Changes check ✅ Passed All changes directly support VoIP push notifications: updates to push token handling, new notification types, call event integration, and media-call service enhancements. No unrelated refactoring or feature additions detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Mar 5, 2026

Codecov Report

❌ Patch coverage is 6.38298% with 88 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.87%. Comparing base (f4dcad4) to head (a976955).
⚠️ Report is 3 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #39386      +/-   ##
===========================================
- Coverage    70.92%   70.87%   -0.05%     
===========================================
  Files         3196     3200       +4     
  Lines       113332   113411      +79     
  Branches     20532    20583      +51     
===========================================
+ Hits         80376    80380       +4     
- Misses       30912    30976      +64     
- Partials      2044     2055      +11     
Flag Coverage Δ
e2e 60.40% <ø> (-0.11%) ⬇️
e2e-api 47.98% <4.16%> (+0.21%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pierre-lehnen-rc pierre-lehnen-rc force-pushed the feat/voip-send-push branch 2 times, most recently from 04c0d47 to 1a94148 Compare March 5, 2026 21:08
@pierre-lehnen-rc pierre-lehnen-rc added this to the 8.3.0 milestone Mar 6, 2026
Base automatically changed from feat/add-voip-push-token to develop March 9, 2026 18:10
@pierre-lehnen-rc pierre-lehnen-rc marked this pull request as ready for review March 9, 2026 19:46
@pierre-lehnen-rc pierre-lehnen-rc requested review from a team as code owners March 9, 2026 19:46
@coderabbitai coderabbitai bot added the type: feature Pull requests that introduces new feature label Mar 9, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 20 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/meteor/app/push/server/definition.ts">

<violation number="1" location="apps/meteor/app/push/server/definition.ts:45">
P1: Strip `useVoipToken` before building the gateway payload, otherwise VoIP notifications can start sending an unexpected field to the push gateway.</violation>
</file>

<file name="ee/packages/media-calls/src/definition/IMediaCallServer.ts">

<violation number="1" location="ee/packages/media-calls/src/definition/IMediaCallServer.ts:7">
P3: Remove this unused duplicate type alias; the same `VoipPushNotificationType` union already exists in `getPushNotificationType.ts`.

(Based on your team's feedback about removing unused types and configuration entries.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/meteor/app/push/server/apn.ts">

<violation number="1" location="apps/meteor/app/push/server/apn.ts:98">
P1: Avoid removing the whole push-token record when a VoIP token fails. Here `userToken` can be `app.voipToken`, and `removeAllByTokenString()` will delete the document that also stores the regular APN token.

(Based on your team's feedback about keeping VoIP tokens distinct from the paired regular token lifecycle.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/meteor/server/services/media-call/service.ts">

<violation number="1" location="apps/meteor/server/services/media-call/service.ts:30">
P2: Handle rejections from `sendVoipPushNotification` here; this async emitter callback currently leaves push-send failures as unhandled promise rejections.</violation>
</file>

<file name="apps/meteor/server/services/push/tokenManagement/registerPushToken.ts">

<violation number="1" location="apps/meteor/server/services/push/tokenManagement/registerPushToken.ts:63">
P1: Regular token refreshes should update an existing paired push-token record and clear `voipToken`; this branch inserts a replacement record instead, and duplicate cleanup deletes the original device `_id`, breaking later VoIP updates.

(Based on your team's feedback about regular push refreshes needing to unset stale VoIP tokens instead of replacing the device record.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/meteor/app/push/server/push.ts">

<violation number="1" location="apps/meteor/app/push/server/push.ts:189">
P2: Only increment `countApn` after confirming there is a token to send to; otherwise VoIP notifications with no `voipToken` are reported as delivered even though nothing was sent.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
ee/packages/media-calls/src/server/CallDirector.ts (1)

218-219: Remove the inline rationale comment.

Line 218 adds an implementation comment. Please keep the rationale in the PR/ADR and leave the assignment self-explanatory in code.

As per coding guidelines, "Avoid code comments in the implementation".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ee/packages/media-calls/src/server/CallDirector.ts` around lines 218 - 219,
Remove the inline rationale comment preceding the _id assignment in CallDirector
(the line with "_id: randomUUID()"); simply leave the assignment
self-explanatory by deleting the comment text so the code reads only the
property assignment. Ensure you only remove the comment and do not alter the
_id: randomUUID() expression or surrounding logic.
apps/meteor/server/services/media-call/push/getPushNotificationType.ts (1)

10-12: Consider edge case: hangupReason === 'rejected' without endedBy.

The check at line 10 uses OR logic: call.endedBy?.id === call.callee.id || call.hangupReason === 'rejected'. If hangupReason is 'rejected' but endedBy is not the callee (e.g., server-initiated rejection), this would still return 'declinedElsewhere'. Verify this matches the intended behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/media-call/push/getPushNotificationType.ts`
around lines 10 - 12, The current condition in getPushNotificationType uses OR
for call.endedBy?.id === call.callee.id || call.hangupReason === 'rejected',
which will classify server-initiated 'rejected' hangups as 'declinedElsewhere';
change the logic so 'rejected' only counts when the callee actually ended the
call. Update the condition around call.endedBy, call.callee.id and
call.hangupReason so that you require endedBy?.id === call.callee.id (or require
call.hangupReason === 'rejected' AND endedBy?.id === call.callee.id) instead of
the current OR expression.
apps/meteor/server/services/push/tokenManagement/registerPushToken.ts (1)

26-41: VoIP token silently dropped when _id is missing.

On line 34, voipToken is only included in the insert if data._id is present: ...(data.voipToken && data._id && { voipToken: data.voipToken }). While the API layer validates this (requiring id when voipToken is provided), if registerPushToken is called from another code path without this validation, the voipToken would be silently ignored.

Consider adding a debug log or defensive check here to make the behavior more explicit.

Optional: Add defensive logging
 async function insertToken(data: PushTokenData): Promise<IPushToken['_id']> {
+	if (data.voipToken && !data._id) {
+		logger.warn({ msg: 'voipToken provided without _id, voipToken will not be stored' });
+	}
+
 	const insertResult = await PushToken.insertToken({
 		...(data._id && { _id: data._id }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/push/tokenManagement/registerPushToken.ts` around
lines 26 - 41, The insertToken function is currently dropping voipToken when
data._id is missing because of the conditional ...(data.voipToken && data._id &&
{ voipToken: data.voipToken }); modify insertToken (and the call to
PushToken.insertToken) to explicitly handle the case where data.voipToken is
present but data._id is missing: either add a defensive logger warning via
logger.warn or throw an Error when data.voipToken && !data._id, so callers are
alerted, and then keep the existing insert behavior (include voipToken only when
_id exists); reference insertToken, PushToken.insertToken, data.voipToken and
data._id when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/meteor/app/api/server/v1/push.ts`:
- Around line 149-151: The response schema's voipToken property must allow
null/absent values: update the response schema where voipToken is declared (the
voipToken property in the push token response schema) to include nullable: true
(or mark it optional/null depending on your schema library) so validation
accepts undefined/null values returned from storage; keep the key name voipToken
and ensure any TypeScript type/interfaces that mirror this schema also allow
string | null | undefined.

In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`:
- Around line 13-24: The projection in getActorUser does not include
freeSwitchExtension but the generic T and later code expect it; update the
options projection object used by Users.findOneById and
Users.findOneByFreeSwitchExtension in getActorUser to include
freeSwitchExtension (e.g. add freeSwitchExtension: 1) so the returned user
satisfies T and user.freeSwitchExtension is available.

In `@apps/meteor/server/services/push/tokenManagement/findDocumentToUpdate.ts`:
- Around line 12-15: The early return in findDocumentToUpdate when
data.voipToken is present forces VoIP payloads into the insert path; change the
guard so it only short-circuits for VoIP-only updates. Concretely, in
findDocumentToUpdate update the condition from "if (data.voipToken) return null"
to only return null when the incoming payload contains voipToken but does NOT
contain any identifying main-token fields (e.g., no data.token, no data.appName,
and no data._id), so that mixed APN+VoIP payloads (token+appName+voipToken) will
fall through and match/update the existing document used by registerPushToken
instead of inserting a new one.

---

Nitpick comments:
In `@apps/meteor/server/services/media-call/push/getPushNotificationType.ts`:
- Around line 10-12: The current condition in getPushNotificationType uses OR
for call.endedBy?.id === call.callee.id || call.hangupReason === 'rejected',
which will classify server-initiated 'rejected' hangups as 'declinedElsewhere';
change the logic so 'rejected' only counts when the callee actually ended the
call. Update the condition around call.endedBy, call.callee.id and
call.hangupReason so that you require endedBy?.id === call.callee.id (or require
call.hangupReason === 'rejected' AND endedBy?.id === call.callee.id) instead of
the current OR expression.

In `@apps/meteor/server/services/push/tokenManagement/registerPushToken.ts`:
- Around line 26-41: The insertToken function is currently dropping voipToken
when data._id is missing because of the conditional ...(data.voipToken &&
data._id && { voipToken: data.voipToken }); modify insertToken (and the call to
PushToken.insertToken) to explicitly handle the case where data.voipToken is
present but data._id is missing: either add a defensive logger warning via
logger.warn or throw an Error when data.voipToken && !data._id, so callers are
alerted, and then keep the existing insert behavior (include voipToken only when
_id exists); reference insertToken, PushToken.insertToken, data.voipToken and
data._id when making the change.

In `@ee/packages/media-calls/src/server/CallDirector.ts`:
- Around line 218-219: Remove the inline rationale comment preceding the _id
assignment in CallDirector (the line with "_id: randomUUID()"); simply leave the
assignment self-explanatory by deleting the comment text so the code reads only
the property assignment. Ensure you only remove the comment and do not alter the
_id: randomUUID() expression or surrounding logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c94fd886-f6f9-491d-9cd5-b6049d59b307

📥 Commits

Reviewing files that changed from the base of the PR and between f5f2374 and 424406a.

📒 Files selected for processing (20)
  • apps/meteor/app/api/server/v1/push.ts
  • apps/meteor/app/push/server/apn.ts
  • apps/meteor/app/push/server/definition.ts
  • apps/meteor/app/push/server/fcm.ts
  • apps/meteor/app/push/server/push.ts
  • apps/meteor/server/services/media-call/logger.ts
  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • apps/meteor/server/services/media-call/service.ts
  • apps/meteor/server/services/push/service.ts
  • apps/meteor/server/services/push/tokenManagement/findDocumentToUpdate.ts
  • apps/meteor/server/services/push/tokenManagement/registerPushToken.ts
  • ee/packages/media-calls/src/definition/IMediaCallServer.ts
  • ee/packages/media-calls/src/internal/agents/UserActorAgent.ts
  • ee/packages/media-calls/src/server/CallDirector.ts
  • ee/packages/media-calls/src/server/MediaCallServer.ts
  • packages/core-typings/src/IPushNotificationConfig.ts
  • packages/core-typings/src/IPushToken.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • packages/models/src/models/PushToken.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • packages/core-typings/src/IPushNotificationConfig.ts
  • ee/packages/media-calls/src/definition/IMediaCallServer.ts
  • apps/meteor/server/services/push/tokenManagement/findDocumentToUpdate.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/core-typings/src/IPushToken.ts
  • apps/meteor/server/services/media-call/logger.ts
  • ee/packages/media-calls/src/server/CallDirector.ts
  • ee/packages/media-calls/src/internal/agents/UserActorAgent.ts
  • apps/meteor/app/push/server/apn.ts
  • apps/meteor/server/services/push/service.ts
  • apps/meteor/app/push/server/fcm.ts
  • apps/meteor/app/push/server/definition.ts
  • apps/meteor/server/services/push/tokenManagement/registerPushToken.ts
  • apps/meteor/server/services/media-call/service.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • packages/models/src/models/PushToken.ts
  • ee/packages/media-calls/src/server/MediaCallServer.ts
  • apps/meteor/app/api/server/v1/push.ts
  • apps/meteor/app/push/server/push.ts
🧠 Learnings (16)
📓 Common learnings
Learnt from: pierre-lehnen-rc
Repo: RocketChat/Rocket.Chat PR: 39174
File: apps/meteor/server/services/push/tokenManagement/registerPushToken.ts:47-49
Timestamp: 2026-03-02T16:21:44.002Z
Learning: In the Rocket.Chat push token system (apps/meteor/server/services/push/tokenManagement/registerPushToken.ts), the main (non-VoIP) token is responsible for keeping authToken, appName, and userId updated. VoIP tokens are supplementary—when a device already has a non-VoIP token registered, VoIP token updates only modify the voipToken field, delegating metadata management to the main token registration flow.
📚 Learning: 2026-03-02T16:21:44.002Z
Learnt from: pierre-lehnen-rc
Repo: RocketChat/Rocket.Chat PR: 39174
File: apps/meteor/server/services/push/tokenManagement/registerPushToken.ts:47-49
Timestamp: 2026-03-02T16:21:44.002Z
Learning: In the Rocket.Chat push token system (apps/meteor/server/services/push/tokenManagement/registerPushToken.ts), the main (non-VoIP) token is responsible for keeping authToken, appName, and userId updated. VoIP tokens are supplementary—when a device already has a non-VoIP token registered, VoIP token updates only modify the voipToken field, delegating metadata management to the main token registration flow.

Applied to files:

  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • packages/core-typings/src/IPushNotificationConfig.ts
  • apps/meteor/server/services/push/tokenManagement/findDocumentToUpdate.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/core-typings/src/IPushToken.ts
  • apps/meteor/app/push/server/apn.ts
  • apps/meteor/server/services/push/service.ts
  • apps/meteor/app/push/server/fcm.ts
  • apps/meteor/app/push/server/definition.ts
  • apps/meteor/server/services/push/tokenManagement/registerPushToken.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • packages/models/src/models/PushToken.ts
  • apps/meteor/app/api/server/v1/push.ts
  • apps/meteor/app/push/server/push.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • packages/core-typings/src/IPushNotificationConfig.ts
  • ee/packages/media-calls/src/definition/IMediaCallServer.ts
  • apps/meteor/server/services/push/tokenManagement/findDocumentToUpdate.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/core-typings/src/IPushToken.ts
  • apps/meteor/server/services/media-call/logger.ts
  • ee/packages/media-calls/src/server/CallDirector.ts
  • ee/packages/media-calls/src/internal/agents/UserActorAgent.ts
  • apps/meteor/app/push/server/apn.ts
  • apps/meteor/server/services/push/service.ts
  • apps/meteor/app/push/server/fcm.ts
  • apps/meteor/app/push/server/definition.ts
  • apps/meteor/server/services/push/tokenManagement/registerPushToken.ts
  • apps/meteor/server/services/media-call/service.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • packages/models/src/models/PushToken.ts
  • ee/packages/media-calls/src/server/MediaCallServer.ts
  • apps/meteor/app/api/server/v1/push.ts
  • apps/meteor/app/push/server/push.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • packages/core-typings/src/IPushNotificationConfig.ts
  • ee/packages/media-calls/src/definition/IMediaCallServer.ts
  • apps/meteor/server/services/push/tokenManagement/findDocumentToUpdate.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/core-typings/src/IPushToken.ts
  • apps/meteor/server/services/media-call/logger.ts
  • ee/packages/media-calls/src/server/CallDirector.ts
  • ee/packages/media-calls/src/internal/agents/UserActorAgent.ts
  • apps/meteor/app/push/server/apn.ts
  • apps/meteor/server/services/push/service.ts
  • apps/meteor/app/push/server/fcm.ts
  • apps/meteor/app/push/server/definition.ts
  • apps/meteor/server/services/push/tokenManagement/registerPushToken.ts
  • apps/meteor/server/services/media-call/service.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • packages/models/src/models/PushToken.ts
  • ee/packages/media-calls/src/server/MediaCallServer.ts
  • apps/meteor/app/api/server/v1/push.ts
  • apps/meteor/app/push/server/push.ts
📚 Learning: 2025-11-17T14:30:36.271Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 37491
File: packages/desktop-api/src/index.ts:17-27
Timestamp: 2025-11-17T14:30:36.271Z
Learning: In the Rocket.Chat desktop API (`packages/desktop-api/src/index.ts`), the `CustomNotificationOptions` type has an optional `id` field by design. Custom notifications dispatched without an ID cannot be closed programmatically using `closeCustomNotification`, and this is intentional.

Applied to files:

  • packages/core-typings/src/IPushNotificationConfig.ts
  • apps/meteor/app/push/server/push.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • apps/meteor/server/services/media-call/logger.ts
  • ee/packages/media-calls/src/server/CallDirector.ts
  • ee/packages/media-calls/src/internal/agents/UserActorAgent.ts
  • apps/meteor/server/services/media-call/service.ts
📚 Learning: 2026-02-20T09:04:55.725Z
Learnt from: Shreyas2004wagh
Repo: RocketChat/Rocket.Chat PR: 38681
File: apps/meteor/server/modules/streamer/streamer.module.ts:307-313
Timestamp: 2026-02-20T09:04:55.725Z
Learning: In apps/meteor/server/modules/streamer/streamer.module.ts, the catch block in sendToManySubscriptions intentionally uses SystemLogger.debug (not error or warn) for per-subscription delivery failures to keep logs less noisy, as this was a deliberate design choice reviewed and approved by KevLehman.

Applied to files:

  • apps/meteor/server/services/media-call/logger.ts
  • apps/meteor/server/services/media-call/service.ts
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • ee/packages/media-calls/src/server/CallDirector.ts
  • apps/meteor/server/services/media-call/service.ts
  • ee/packages/media-calls/src/server/MediaCallServer.ts
📚 Learning: 2026-02-26T19:22:36.646Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.tsx:40-40
Timestamp: 2026-02-26T19:22:36.646Z
Learning: In packages/ui-voip/src/views/CallHistoryContextualbar/CallHistoryActions.tsx, when the media session state is 'unavailable', the voiceCall action is not included in the actions object passed to CallHistoryActions, so it won't appear in the menu at all. The action filtering happens upstream before getItems is called, preventing any tooltip confusion for the unavailable state.

Applied to files:

  • ee/packages/media-calls/src/internal/agents/UserActorAgent.ts
  • apps/meteor/server/services/media-call/service.ts
  • ee/packages/media-calls/src/server/MediaCallServer.ts
📚 Learning: 2026-02-24T19:09:09.561Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38974
File: apps/meteor/app/api/server/v1/im.ts:220-221
Timestamp: 2026-02-24T19:09:09.561Z
Learning: In RocketChat/Rocket.Chat OpenAPI migration PRs for apps/meteor/app/api/server/v1 endpoints, maintainers prefer to avoid any logic changes; style-only cleanups (like removing inline comments) may be deferred to follow-ups to keep scope tight.

Applied to files:

  • apps/meteor/app/push/server/apn.ts
  • apps/meteor/server/services/push/service.ts
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/server/services/push/service.ts
  • apps/meteor/app/push/server/fcm.ts
  • apps/meteor/app/push/server/definition.ts
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • apps/meteor/server/services/push/service.ts
  • packages/models/src/models/PushToken.ts
📚 Learning: 2026-02-25T20:10:16.987Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38913
File: packages/ddp-client/src/legacy/types/SDKLegacy.ts:34-34
Timestamp: 2026-02-25T20:10:16.987Z
Learning: In the RocketChat/Rocket.Chat monorepo, packages/ddp-client and apps/meteor do not use TypeScript project references. Module augmentations in apps/meteor (e.g., declare module 'rocket.chat/rest-typings') are not visible when compiling packages/ddp-client in isolation, which is why legacy SDK methods that depend on OperationResult types for OpenAPI-migrated endpoints must remain commented out.

Applied to files:

  • packages/model-typings/src/models/IPushTokenModel.ts
  • packages/models/src/models/PushToken.ts
  • apps/meteor/app/api/server/v1/push.ts
📚 Learning: 2026-03-09T18:39:14.020Z
Learnt from: Harxhit
Repo: RocketChat/Rocket.Chat PR: 39476
File: apps/meteor/server/methods/addAllUserToRoom.ts:0-0
Timestamp: 2026-03-09T18:39:14.020Z
Learning: In apps/meteor/server/methods/addAllUserToRoom.ts, the implementation uses a single cursor pass (Users.find(userFilter).batchSize(100)) that collects both the full user objects (collectedUsers: IUser[]) and their usernames (usernames: string[]) in one iteration. `beforeAddUserToRoom` is then called once with the full usernames batch (preserving batch-validation semantics), and the subsequent subscription/message processing loop iterates over the same stable `collectedUsers` array — no second DB query is made. This avoids any race condition between validation and processing while preserving the original batch-validation behavior.

Applied to files:

  • packages/models/src/models/PushToken.ts
📚 Learning: 2026-02-23T17:53:06.802Z
Learnt from: ggazzo
Repo: RocketChat/Rocket.Chat PR: 35995
File: apps/meteor/app/api/server/v1/rooms.ts:1107-1112
Timestamp: 2026-02-23T17:53:06.802Z
Learning: During PR reviews that touch endpoint files under apps/meteor/app/api/server/v1, enforce strict scope: if a PR targets a specific endpoint (e.g., rooms.favorite), do not propose changes to unrelated endpoints (e.g., rooms.invite) unless maintainers explicitly request them. Focus feedback on the touched endpoint's behavior, API surface, and related tests; avoid broad cross-endpoint changes in the same PR unless requested.

Applied to files:

  • apps/meteor/app/api/server/v1/push.ts
📚 Learning: 2026-02-24T19:09:01.522Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38974
File: apps/meteor/app/api/server/v1/im.ts:220-221
Timestamp: 2026-02-24T19:09:01.522Z
Learning: In Rocket.Chat OpenAPI migration PRs for endpoints under apps/meteor/app/api/server/v1, avoid introducing logic changes. Only perform scope-tight changes that preserve behavior; style-only cleanups (e.g., removing inline comments) may be deferred to follow-ups to keep the migration PR focused.

Applied to files:

  • apps/meteor/app/api/server/v1/push.ts
🔇 Additional comments (27)
apps/meteor/app/push/server/fcm.ts (3)

11-15: LGTM! Optional notification fields support VoIP use cases.

Making title and body optional in FCMNotificationField correctly aligns with the broader push notification changes where VoIP notifications may not include visible notification content.


142-154: LGTM! Conditional notification field construction is correct.

The conditional spread pattern at lines 143-144 ensures title and body are only included when present, and line 149 correctly omits the notification field entirely when empty. This allows data-only FCM messages for VoIP.


186-189: LGTM! Token removal simplified to use string directly.

The change to pass token directly to _removeToken aligns with the updated PushToken.removeAllByTokenString API that accepts a raw token string instead of an object structure.

apps/meteor/server/services/media-call/service.ts (2)

16-17: LGTM! Clean import structure for VoIP push integration.

The imports correctly bring in the module-scoped logger and the new sendVoipPushNotification handler.


30-30: LGTM! Event wiring for VoIP push notifications follows established patterns.

The pushNotificationRequest event listener correctly delegates to sendVoipPushNotification, following the same pattern as the existing signalRequest, callUpdated, and historyUpdate handlers.

apps/meteor/server/services/push/service.ts (1)

39-44: LGTM! Duplicate token removal parameters align with updated token management API.

The removeDuplicateTokens call correctly passes the required fields (_id, token, appName, authToken) for identifying and removing duplicate tokens while preserving the newly registered token.

ee/packages/media-calls/src/internal/agents/UserActorAgent.ts (3)

54-64: LGTM! End notification correctly gated for callee role.

The 'end' push notification is appropriately sent only for the callee role before sending the hangup signal, allowing the callee's device to handle call termination.


74-85: LGTM! New call notification correctly follows signal dispatch.

The 'new' push notification is sent after sendSignal(buildNewCallSignal(...)), which is the correct order—the signal goes first to online clients, then the push notification serves as a backup to wake offline devices.


24-52: Push notification timing is intentional and correctly designed.

The onCallAccepted method is explicitly documented in IMediaCallAgent.ts as being "called when the call was accepted, even if the webrtc negotiation is pending." The early return when the offer is unavailable (lines 38-42) is by design—it allows the 'answer' push event to signal call acceptance independently of WebRTC negotiation readiness, which is handled separately via onRemoteDescriptionChanged when the SDP becomes available. No changes needed.

			> Likely an incorrect or invalid review comment.
apps/meteor/app/push/server/definition.ts (1)

20-46: LGTM! Type changes enable VoIP-specific push notification flows.

Making from, title, and text optional correctly supports VoIP push notifications that may be data-only. The new useVoipToken field enables routing to the dedicated VoIP push token. The context snippets confirm that push.ts, apn.ts, and fcm.ts all handle these optional fields with appropriate conditional checks.

ee/packages/media-calls/src/server/MediaCallServer.ts (2)

9-14: LGTM! Import correctly expanded for VoIP push notification types.

The import now includes VoipPushNotificationEventType which is used in the new sendPushNotification method signature.


78-82: LGTM! Push notification method follows established patterns.

The sendPushNotification method correctly follows the same pattern as sendSignal, reportCallUpdate, and updateCallHistory—logging debug information and emitting an event for the service layer to handle.

apps/meteor/app/push/server/apn.ts (3)

37-42: LGTM! VoIP-specific APN configuration is correct.

The 60-second expiry for VoIP notifications is appropriate for time-sensitive call alerts, and pushType: 'voip' is the correct setting for iOS VoIP push notifications via PushKit.


59-79: LGTM! Conditional field assignments handle optional values correctly.

Each field (category, body, title, from) is now guarded with an existence check before assignment, preventing undefined values from being set on the APN notification object.


27-27: LGTM! Token removal callback simplified to accept string directly.

The _removeToken signature change to accept a plain string aligns with the updated PushToken.removeAllByTokenString API used by the caller in push.ts.

Also applies to: 98-98

apps/meteor/server/services/media-call/push/getPushNotificationType.ts (1)

5-23: No actionable changes needed. The function correctly handles the 'answeredElsewhere' notification type through an early return guard in the consuming function (sendVoipPushNotification.ts, line 69): when call.acceptedAt is set, notifications are only sent for the 'answer' event, not for subsequent 'end' events. The 'answeredElsewhere' type is therefore only returned when appropriate—to notify other devices that a call was answered—and the concern about notifying the device that completed the call is prevented by the guard condition upstream.

apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts (2)

85-103: LGTM!

The notification payload structure is well-designed with appropriate fields for VoIP call notifications. The conditional skipTokenId using contractId correctly prevents sending state-change notifications to the device that already handled the call.


56-80: LGTM!

The early return logic correctly handles edge cases: skipping notifications for already-accepted calls (except 'answer' events), and ensuring state consistency between event types and notification types.

apps/meteor/server/services/push/tokenManagement/registerPushToken.ts (2)

12-24: LGTM!

The canModifyTokenDocument logic correctly ensures VoIP tokens can only be modified via explicit _id match, preventing accidental overwrites. This aligns with the design where VoIP tokens are supplementary to the main token. Based on learnings: "VoIP tokens are supplementary—when a device already has a non-VoIP token registered, VoIP token updates only modify the voipToken field, delegating metadata management to the main token registration flow."


60-68: LGTM!

The refactored registerPushToken function cleanly orchestrates the token registration flow with proper separation of concerns between finding, inserting, and updating tokens.

apps/meteor/app/api/server/v1/push.ts (2)

166-170: LGTM!

Good defensive validation ensuring voipToken cannot be registered without a device id. This prevents the silent token drop issue in the downstream registration logic.


24-30: LGTM!

Good refactoring to use shared IPushTokenTypes and pushTokenTypes from core-typings, ensuring consistency across the codebase. The cleanTokenResult function properly sanitizes the response.

Also applies to: 80-97

apps/meteor/app/push/server/push.ts (5)

331-346: LGTM!

The VoIP-specific retry limit (maxTries: 1 for VoIP vs PUSH_GATEWAY_MAX_ATTEMPTS for regular notifications) is a sensible design choice. VoIP notifications are time-sensitive, so avoiding extended retries prevents stale call notifications. The token existence check on line 343 properly handles cases where a VoIP token may not be registered.


356-384: LGTM!

The skipTokenId option is well-implemented, allowing VoIP notifications to exclude the device that already handled the call action. The conditional between findTokensByUserIdExceptId and findAllTokensByUserId cleanly handles both cases.


429-454: LGTM!

The validation schema changes correctly accommodate VoIP notifications which may not have from, title, or text fields. The addition of useVoipToken validation ensures type safety.


188-196: LGTM!

The APN notification path correctly handles VoIP tokens: selecting the appropriate token based on useVoipToken, setting the topic suffix to .voip for VoIP notifications, and properly guarding against missing tokens before sending.


170-174: LGTM!

The removeToken method signature change to accept a token string directly simplifies the interface and aligns with how it's called from APN/FCM error handlers. The error handling with logging prevents unhandled promise rejections.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts (1)

13-22: ⚠️ Potential issue | 🟡 Minor

Include freeSwitchExtension in the actor projection.

Line 45 falls back to user.freeSwitchExtension, but the projection on Line 16 only fetches name and username. For SIP actors that makes the last user-based fallback always undefined.

🩹 Proposed fix
-	const options = { projection: { name: 1, username: 1 } };
+	const options = { projection: { name: 1, username: 1, freeSwitchExtension: 1 } };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`
around lines 13 - 22, The projection for getActorUser currently only requests
name and username, but later code expects freeSwitchExtension (causing SIP
actors to get undefined); update the projection object in getActorUser (used by
Users.findOneById and Users.findOneByFreeSwitchExtension) to include
freeSwitchExtension so the returned user documents contain that field for the
SIP fallback logic to work correctly.
🧹 Nitpick comments (3)
apps/meteor/app/push/server/push.ts (1)

310-317: Make the gateway payload mapping explicit.

This subtractive spread forwards every future PendingPushNotification field unless someone remembers to keep the omit list in sync. That's brittle at an external API boundary. Prefer an explicit mapper or pick() list and the new inline comment goes away too.

As per coding guidelines, "Avoid code comments in the implementation".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/app/push/server/push.ts` around lines 310 - 317, The current
getGatewayNotificationData function uses a subtractive spread on
PendingPushNotification (via destructuring priority/useVoipToken) which will
silently forward any future fields to the gateway; replace this brittle approach
with an explicit mapping: enumerate and copy only the allowed properties into
the returned GatewayNotification payload (i.e., explicitly pick fields such as
title, body, data, badge, sound, etc.) inside getGatewayNotificationData and
remove the subtractive spread and the comment so the mapping is explicit and
self-documenting for GatewayNotification.
apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts (2)

68-75: Extract these state guards instead of documenting them inline.

The new comments are explaining branching that would read better as small predicates like shouldSkipAcceptedCallEvent() and shouldSkipStaleIncomingCall(). That keeps the flow readable without carrying implementation comments.

As per coding guidelines, "Avoid code comments in the implementation".

Also applies to: 101-102

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`
around lines 68 - 75, Extract the inline state guards into named predicate
helpers to remove the explanatory comments: create
shouldSkipAcceptedCallEvent(call, event) that returns true when call.acceptedAt
is set and event !== 'answer', and create shouldSkipStaleIncomingCall(call,
event) which uses getPushNotificationType(call) and returns true when event ===
'new' and type !== 'incoming_call'; then replace the two inline if-checks in
sendVoipPushNotification with these predicates (and remove the comments), and
update any related checks around the other similar branch (the other occurrence
around the existing checks) to call the new helpers for clarity.

85-87: Don't count this as sent yet.

Line 85 increments metrics.notificationsSent before any token lookup or dispatch, and Push.send() catches its own failures in apps/meteor/app/push/server/push.ts Lines 500-508. Right now this metric reports attempts as successful sends, which will make VoIP rollout telemetry hard to trust.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`
around lines 85 - 87, metrics.notificationsSent is being incremented before the
push is actually dispatched and Push.send internally catches failures; move the
metrics.notificationsSent.inc call so it runs only after a successful send.
Specifically, in sendVoipPushNotification (around where
metrics.notificationsSent.inc and Push.send are called), await Push.send(...)
and only call metrics.notificationsSent.inc({ notification_type: 'mobile' })
when Push.send resolves without error (or when the send result indicates
success); do not increment on start or on caught failures from
apps/meteor/app/push/server/push.ts send handler.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/meteor/app/push/server/push.ts`:
- Around line 300-306: The retry branch in sendGatewayPush currently schedules
exponential backoff retries even for VoIP pushes (notification.useVoipToken),
allowing stale incoming_call re-delivery; change the logic in sendGatewayPush to
skip scheduling delayed retries when notification.useVoipToken is true (do not
call setTimeout with sendGatewayPush for VoIP pushes) and instead avoid retrying
(or handle VoIP retries synchronously if a different behavior is required).
Update both retry sites (the setTimeout call using tries/maxTries around the
exponential backoff and the similar block at lines ~331-334) to check
notification.useVoipToken and return without queuing a delayed retry when it's
true. Ensure you reference the notification.useVoipToken flag and the
sendGatewayPush(...) retry invocation so the fix is applied to both locations.

---

Duplicate comments:
In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`:
- Around line 13-22: The projection for getActorUser currently only requests
name and username, but later code expects freeSwitchExtension (causing SIP
actors to get undefined); update the projection object in getActorUser (used by
Users.findOneById and Users.findOneByFreeSwitchExtension) to include
freeSwitchExtension so the returned user documents contain that field for the
SIP fallback logic to work correctly.

---

Nitpick comments:
In `@apps/meteor/app/push/server/push.ts`:
- Around line 310-317: The current getGatewayNotificationData function uses a
subtractive spread on PendingPushNotification (via destructuring
priority/useVoipToken) which will silently forward any future fields to the
gateway; replace this brittle approach with an explicit mapping: enumerate and
copy only the allowed properties into the returned GatewayNotification payload
(i.e., explicitly pick fields such as title, body, data, badge, sound, etc.)
inside getGatewayNotificationData and remove the subtractive spread and the
comment so the mapping is explicit and self-documenting for GatewayNotification.

In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`:
- Around line 68-75: Extract the inline state guards into named predicate
helpers to remove the explanatory comments: create
shouldSkipAcceptedCallEvent(call, event) that returns true when call.acceptedAt
is set and event !== 'answer', and create shouldSkipStaleIncomingCall(call,
event) which uses getPushNotificationType(call) and returns true when event ===
'new' and type !== 'incoming_call'; then replace the two inline if-checks in
sendVoipPushNotification with these predicates (and remove the comments), and
update any related checks around the other similar branch (the other occurrence
around the existing checks) to call the new helpers for clarity.
- Around line 85-87: metrics.notificationsSent is being incremented before the
push is actually dispatched and Push.send internally catches failures; move the
metrics.notificationsSent.inc call so it runs only after a successful send.
Specifically, in sendVoipPushNotification (around where
metrics.notificationsSent.inc and Push.send are called), await Push.send(...)
and only call metrics.notificationsSent.inc({ notification_type: 'mobile' })
when Push.send resolves without error (or when the send result indicates
success); do not increment on start or on caught failures from
apps/meteor/app/push/server/push.ts send handler.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1c0d8831-8f61-4124-9a18-d7e1d582d852

📥 Commits

Reviewing files that changed from the base of the PR and between b066854 and cb4338f.

📒 Files selected for processing (6)
  • apps/meteor/app/push/server/push.ts
  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/core-typings/src/index.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • packages/models/src/models/PushToken.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/models/src/models/PushToken.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • packages/core-typings/src/index.ts
  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • apps/meteor/app/push/server/push.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: pierre-lehnen-rc
Repo: RocketChat/Rocket.Chat PR: 39174
File: apps/meteor/server/services/push/tokenManagement/registerPushToken.ts:47-49
Timestamp: 2026-03-02T16:21:44.002Z
Learning: In the Rocket.Chat push token system (apps/meteor/server/services/push/tokenManagement/registerPushToken.ts), the main (non-VoIP) token is responsible for keeping authToken, appName, and userId updated. VoIP tokens are supplementary—when a device already has a non-VoIP token registered, VoIP token updates only modify the voipToken field, delegating metadata management to the main token registration flow.
📚 Learning: 2026-03-02T16:21:44.002Z
Learnt from: pierre-lehnen-rc
Repo: RocketChat/Rocket.Chat PR: 39174
File: apps/meteor/server/services/push/tokenManagement/registerPushToken.ts:47-49
Timestamp: 2026-03-02T16:21:44.002Z
Learning: In the Rocket.Chat push token system (apps/meteor/server/services/push/tokenManagement/registerPushToken.ts), the main (non-VoIP) token is responsible for keeping authToken, appName, and userId updated. VoIP tokens are supplementary—when a device already has a non-VoIP token registered, VoIP token updates only modify the voipToken field, delegating metadata management to the main token registration flow.

Applied to files:

  • packages/core-typings/src/index.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • apps/meteor/app/push/server/push.ts
📚 Learning: 2026-02-25T20:10:16.987Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38913
File: packages/ddp-client/src/legacy/types/SDKLegacy.ts:34-34
Timestamp: 2026-02-25T20:10:16.987Z
Learning: In the RocketChat/Rocket.Chat monorepo, packages/ddp-client and apps/meteor do not use TypeScript project references. Module augmentations in apps/meteor (e.g., declare module 'rocket.chat/rest-typings') are not visible when compiling packages/ddp-client in isolation, which is why legacy SDK methods that depend on OperationResult types for OpenAPI-migrated endpoints must remain commented out.

Applied to files:

  • packages/core-typings/src/index.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • packages/core-typings/src/index.ts
  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • apps/meteor/app/push/server/push.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • packages/core-typings/src/index.ts
  • apps/meteor/server/services/media-call/push/getPushNotificationType.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
  • packages/model-typings/src/models/IPushTokenModel.ts
  • apps/meteor/app/push/server/push.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • packages/model-typings/src/models/IPushTokenModel.ts
📚 Learning: 2025-11-17T14:30:36.271Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 37491
File: packages/desktop-api/src/index.ts:17-27
Timestamp: 2025-11-17T14:30:36.271Z
Learning: In the Rocket.Chat desktop API (`packages/desktop-api/src/index.ts`), the `CustomNotificationOptions` type has an optional `id` field by design. Custom notifications dispatched without an ID cannot be closed programmatically using `closeCustomNotification`, and this is intentional.

Applied to files:

  • apps/meteor/app/push/server/push.ts
🔇 Additional comments (2)
packages/core-typings/src/index.ts (1)

30-30: LGTM!

The change from export type * to export * correctly exposes the runtime pushTokenTypes const array from IPushToken.ts, which is needed for the VoIP push notification feature. This follows the established pattern used for other modules that export runtime values (e.g., UserStatus on line 13 and IServerEvent on line 28).

apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts (1)

101-102: I need the review comment to be provided in order to rewrite it. Please share the original review comment that needs to be verified and rewritten.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts (1)

18-24: Add exhaustive handling for actor types.

The switch statement lacks a default case, causing the function to implicitly return undefined when actor.type is neither 'user' nor 'sip'. This contradicts the declared return type Promise<T | null>.

Proposed fix
 	switch (actor.type) {
 		case 'user':
 			return Users.findOneById<T>(actor.id, options);
 		case 'sip':
 			return Users.findOneByFreeSwitchExtension<T>(actor.id, options);
+		default:
+			return null;
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`
around lines 18 - 24, The switch over actor.type in sendVoipPushNotification.ts
only handles 'user' and 'sip' and can implicitly return undefined; update the
switch in the function so it is exhaustive: add a default branch that returns
null (or explicitly Promise.resolve(null) to match Promise<T | null>), or
replace the default with an assertUnreachable check/cast if you prefer stricter
typing. Make the change near the existing branches for Users.findOneById<T> and
Users.findOneByFreeSwitchExtension<T> so unknown actor.type values reliably
produce null (or an explicit runtime error if you choose assertUnreachable).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`:
- Around line 106-110: The statement in sendVoipPushNotification that calls
sendVoipPushNotificationAsync(...).catch(...) is missing a trailing semicolon;
update the end of the void sendVoipPushNotificationAsync(callId,
event).catch((err) => { logger.error({ msg: 'Failed to send VoIP push
notification', err, callId, event }); }) line to include a semicolon so the
sendVoipPushNotification function ends with a proper statement terminator.
- Line 90: The file uses Meteor.absoluteUrl() but does not import Meteor; add
the missing import "import { Meteor } from 'meteor/meteor';" at the top of the
module (matching other files' import style) so Meteor.absoluteUrl() resolves
correctly and avoids a ReferenceError.

---

Nitpick comments:
In `@apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts`:
- Around line 18-24: The switch over actor.type in sendVoipPushNotification.ts
only handles 'user' and 'sip' and can implicitly return undefined; update the
switch in the function so it is exhaustive: add a default branch that returns
null (or explicitly Promise.resolve(null) to match Promise<T | null>), or
replace the default with an assertUnreachable check/cast if you prefer stricter
typing. Make the change near the existing branches for Users.findOneById<T> and
Users.findOneByFreeSwitchExtension<T> so unknown actor.type values reliably
produce null (or an explicit runtime error if you choose assertUnreachable).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3fda886b-c087-4e7f-bcac-abd95d1bcf92

📥 Commits

Reviewing files that changed from the base of the PR and between cb4338f and 7096fdf.

📒 Files selected for processing (2)
  • apps/meteor/app/api/server/v1/push.ts
  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/meteor/app/api/server/v1/push.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 🔨 Test Unit / Unit Tests
  • GitHub Check: 🔎 Code Check / TypeScript
  • GitHub Check: 🔎 Code Check / Code Lint
  • GitHub Check: 📦 Meteor Build (coverage)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: pierre-lehnen-rc
Repo: RocketChat/Rocket.Chat PR: 39174
File: apps/meteor/server/services/push/tokenManagement/registerPushToken.ts:47-49
Timestamp: 2026-03-02T16:21:44.002Z
Learning: In the Rocket.Chat push token system (apps/meteor/server/services/push/tokenManagement/registerPushToken.ts), the main (non-VoIP) token is responsible for keeping authToken, appName, and userId updated. VoIP tokens are supplementary—when a device already has a non-VoIP token registered, VoIP token updates only modify the voipToken field, delegating metadata management to the main token registration flow.
📚 Learning: 2026-03-02T16:21:44.002Z
Learnt from: pierre-lehnen-rc
Repo: RocketChat/Rocket.Chat PR: 39174
File: apps/meteor/server/services/push/tokenManagement/registerPushToken.ts:47-49
Timestamp: 2026-03-02T16:21:44.002Z
Learning: In the Rocket.Chat push token system (apps/meteor/server/services/push/tokenManagement/registerPushToken.ts), the main (non-VoIP) token is responsible for keeping authToken, appName, and userId updated. VoIP tokens are supplementary—when a device already has a non-VoIP token registered, VoIP token updates only modify the voipToken field, delegating metadata management to the main token registration flow.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2025-12-18T15:18:31.688Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37773
File: apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx:24-34
Timestamp: 2025-12-18T15:18:31.688Z
Learning: In apps/meteor/client/views/mediaCallHistory/MediaCallHistoryInternal.tsx, for internal call history items, the item.contactId is guaranteed to always match either the caller.id or callee.id in the call data, so the contact resolution in getContact will never result in undefined.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts
🔇 Additional comments (1)
apps/meteor/server/services/media-call/push/sendVoipPushNotification.ts (1)

26-54: LGTM!

The fallback chain for actor name resolution and conditional avatar URL generation are well-structured.

Co-authored-by: Kevin Aleman <kaleman960@gmail.com>
authRequired: true,
},
async function action() {
const { token } = this.bodyParams;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to remove voip token as well, right?

@scuciatto scuciatto modified the milestones: 8.3.0, 8.4.0 Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature Pull requests that introduces new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants