Skip to content

feat: add call answering, outgoing calls and audio injection#201825

Open
alpersamur3 wants to merge 1 commit into
wwebjs:mainfrom
alpersamur3:feat/call-answering-audio
Open

feat: add call answering, outgoing calls and audio injection#201825
alpersamur3 wants to merge 1 commit into
wwebjs:mainfrom
alpersamur3:feat/call-answering-audio

Conversation

@alpersamur3

Copy link
Copy Markdown

Description

Adds first-class call support: answer incoming calls, place outgoing calls, and
inject audio (e.g. a text-to-speech file) into an active call. Also fixes call
handling that no longer works on current WhatsApp Web.

New Call methods:

  • accept() — answer the call (audio only by default, even for incoming video calls)
  • end() — hang up an ongoing call
  • playAudio(media) — play a MessageMedia/base64 audio clip into the call so the
    other party hears it; resolves when playback finishes, so clips can be chained
  • isConnected() — whether the other party has answered

New Client methods:

  • call(chatId, { video, waitForAnswer, answerTimeout }) — place an outgoing
    voice/video call; with waitForAnswer: true it resolves once the callee answers
  • getActiveCall() — the ongoing call (ringing/placing/connected), or null

Fixes:

  • Incoming call event: current WhatsApp Web surfaces calls through
    WAWebCallCollection's activeCall attribute; the previous internal-map set
    hook no longer fires, so client.on('call') never triggered. Now listens to
    change:activeCall (the map hook is kept as a fallback).
  • Call.reject(): the previous signaling stanza no longer rejects on current
    WhatsApp Web; it now goes through the voip stack's rejectCall().

Audio is captured from a synthetic Web Audio stream (WhatsApp's getUserMedia is
overridden), so no microphone is required and it works headless. The browser
flags needed for this are added automatically.

Related Issue(s)

fixes #201746

Testing Summary

Test Details

Verified end-to-end against a real phone with live calls.

Outgoing voice call + audio + hang up:

const call = await client.call('1XXXXXXXXXX', { waitForAnswer: true });
if (await call.isConnected()) {
    await call.playAudio(MessageMedia.fromFilePath('./hello.wav'));
    await call.end();
}

Incoming call handling:

client.on('call', async (call) => {
    if (call.fromMe) return;
    await call.accept();                 // or await call.reject()
    await call.playAudio(MessageMedia.fromFilePath('./welcome.wav'));
    await call.end();
});

Confirmed: incoming voice/video calls fire the call event; accept, reject,
end, sequential playAudio and getActiveCall all work; the callee hears the
injected audio; video calls are answered audio-only; everything also works with a
headless browser and no microphone.

Note: the repo's npm test integration suite requires a configured live test
account (WWEBJS_TEST_REMOTE_ID etc.) and only covers messaging/groups, not
calls, so verification was done manually with real calls as shown above.

Environment

  • Machine OS: Windows 11
  • Phone OS: Android 16
  • Library Version: 1.34.7
  • WhatsApp Web Version: 2.3000.1017054665
  • Browser Type and Version: Google Chrome 149
  • Node Version: 22.17.0

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)

Checklist

  • My code follows the style guidelines of this project.
  • All new and existing tests pass (npm test) — the integration suite needs a live test account and only covers messaging/groups; verified manually with real calls.
  • Typings (index.d.ts) have been updated.
  • Usage examples (example.js) / documentation have been updated.

@github-actions github-actions Bot added api changes API modifications typings Type definitions utility Utility code labels Jul 12, 2026
@alpersamur3

Copy link
Copy Markdown
Author

Heads-up: I've built video support on top of this, in a follow-up branch (feat/call-video-injection on my fork). It reuses the same synthetic-media approach and adds:

  • Call.showImage(media) and Call.playVideo(media, { loop })
  • orientation (landscape / portrait / auto) and resolution options on Client.call / Call.accept
  • Call.setVideoResolution(px) to change quality mid-call

Verified live end-to-end (portrait/landscape, mid-call source + resolution changes, video + injected audio together), including headless and after a cold restart.

Once this PR is merged I'll rebase that branch onto main and open it as a separate PR to keep the diffs clean — happy to fold it in here instead if you'd prefer.

adds the ability to answer incoming calls, place outgoing calls and inject
audio into an active call, plus fixes that make call handling work on
current WhatsApp Web.

call:
- accept() answers a call (audio only by default, even for video calls)
- end() hangs up an ongoing call
- playAudio(media) streams a MessageMedia/base64 audio clip to the other party
- isConnected() reports whether the other party has answered

client:
- call(chatId, { video, waitForAnswer, answerTimeout }) places an outgoing call
- getActiveCall() returns the ongoing call, if any

fixes:
- Incoming "call" event: current WhatsApp Web reports calls through
  WAWebCallCollection's activeCall attribute; the previous internal-map hook
  no longer fires. The map hook is kept as a fallback.
- Call.reject() now goes through the voip stack, which the previous signaling
  stanza no longer triggers on current WhatsApp Web.

audio is captured through a synthetic Web Audio stream, so no microphone is
required and it works headless. Adds the browser flags needed for this.
@alpersamur3
alpersamur3 force-pushed the feat/call-answering-audio branch from 6e791dd to 63ec90e Compare July 12, 2026 19:06
@alpersamur3

Copy link
Copy Markdown
Author

@aliyss @lindionez Hi! Could you please take a look at this PR when you have a moment? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api changes API modifications typings Type definitions utility Utility code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reject Call don't work

1 participant