Skip to content

feat: Add BroadSoft Access-Side Extensions Support (Auto-Answer and Remote Control) - #1118

Open
rasonyang wants to merge 8 commits into
onsip:mainfrom
rasonyang:feat/broadsoft-extensions
Open

feat: Add BroadSoft Access-Side Extensions Support (Auto-Answer and Remote Control)#1118
rasonyang wants to merge 8 commits into
onsip:mainfrom
rasonyang:feat/broadsoft-extensions

Conversation

@rasonyang

Copy link
Copy Markdown

Summary

This PR adds support for BroadSoft Access-Side Extensions, implementing two key features:

  1. Auto-Answer - Automatic call answering based on Call-Info header with answer-after parameter
  2. Remote Control - Talk Events - Remote call control via NOTIFY messages with Event: talk

Changes

Core API (src/api/broadsoft/)

  • auto-answer.ts - Auto-answer logic for Call-Info header parsing and delayed answering
  • call-info-parser.ts - Parser for Call-Info header with answer-after parameter
  • remote-control.ts - Handler for remote control NOTIFY events (talk/mute)
  • types.ts - TypeScript types and enums for BroadSoft extensions
  • index.ts - Public API exports with helper functions

Demo Application

  • demo/demo-broadsoft.html - Interactive demo UI for testing BroadSoft extensions
  • demo/demo-broadsoft.ts - Full-featured demo implementation with FreeSWITCH integration
  • demo/demo-broadsoft.css - Styled UI for demo application

Tests

  • test/spec/api/broadsoft/auto-answer.spec.ts - Unit tests for auto-answer functionality
  • test/spec/api/broadsoft/call-info-parser.spec.ts - Unit tests for Call-Info header parsing
  • test/spec/api/broadsoft/remote-control.spec.ts - Unit tests for remote control events

Documentation

  • src/api/broadsoft/README.md - Comprehensive API documentation with usage examples
  • examples/broadsoft-extensions.ts - Code examples demonstrating integration
  • integration-tests/FREESWITCH.md - FreeSWITCH integration testing guide
  • integration-tests/README.md - General integration testing documentation

Features

Auto-Answer

  • Parses Call-Info: <sip:domain>; answer-after=N header from incoming INVITE
  • Configurable delay before auto-answering (default: from header)
  • Optional callbacks for before/after auto-answer events
  • Helper functions: shouldAutoAnswer(), getAutoAnswerDelay(), handleAutoAnswer()

Remote Control

  • Handles NOTIFY messages with Event: talk
  • Automatic call answering/resuming when talk event received
  • Configurable callbacks for UI updates
  • Helper functions: isBroadSoftNotification(), handleRemoteControlNotification()

Testing

Run the test suite:

npm test

Run the demo application:
npm run build-demo
# Open demo/demo-broadsoft.html in browser

Integration with FreeSWITCH

Test auto-answer:
originate {sip_h_Call-Info=<sip:${domain}>; answer-after=0}user/1000@${domain} &echo

Test remote control:
uuid_phone_event <uuid> talk

See integration-tests/FREESWITCH.md for complete testing instructions.

API Usage

import { BroadSoft, Invitation } from "sip.js";

// Auto-answer configuration
const autoAnswerOptions: BroadSoft.AutoAnswerOptions = {
  enabled: true,
  onBeforeAutoAnswer: (delaySeconds) => console.log(`Auto-answering in ${delaySeconds}s`),
  onAfterAutoAnswer: () => console.log("Call answered")
};

userAgent.delegate = {
  onInvite: (invitation: Invitation) => {
    if (BroadSoft.shouldAutoAnswer(invitation)) {
      BroadSoft.handleAutoAnswer(invitation, autoAnswerOptions);
    }

    invitation.delegate = {
      onNotify: (notification) => {
        if (BroadSoft.isBroadSoftNotification(notification)) {
          BroadSoft.handleRemoteControlNotification(invitation, notification, {
            enabled: true,
            onTalkEvent: (action) => console.log(`Talk event: ${action}`)
          });
        }
        notification.accept();
      }
    };
  }
};

Breaking Changes

None. This is a new feature that is opt-in.

Checklist

- Core API implementation
- Unit tests with >90% coverage
- Demo application
- API documentation
- Integration testing guide
- Example code

杨雄辉 and others added 8 commits October 16, 2025 15:11
- BroadSoftEvent.Hold, HoldAction, HoldNotifyBody types
- applyHoldAction: re-INVITE with a=sendonly when Established only
- hold dispatch in handleRemoteControlNotify/Notification with onHoldEvent callback
- case-insensitive answer-after parameter matching per SIP rules
- prepare script so git dependencies build lib/ on install
…gn API contracts

Core regression fixes:
- Invitation.progress() now installs a NOTIFY-only delegate on early dialogs
  so core defaults keep answering early-dialog BYE/INFO/MESSAGE/re-INVITE
  (200/469/200/488) instead of dropping them without a response
- Session.onNotifyRequest restores the Established-only rule with a narrow
  carve-out for BroadSoft Event: talk/hold during Initial/Establishing
- handleRemoteControlNotify() now applies SIP signaling (accept/resume/hold)
  as documented, matching handleRemoteControlNotification()

Auto-answer hardening:
- answer-after is strictly parsed (non-negative integers only); malformed
  values (1abc, -5, 1.9) no longer schedule an answer
- delays are capped at 2^31-1 ms so huge values cannot overflow the timer
  and answer immediately
- handleAutoAnswer returns a cancellation handle and auto-cancels when the
  invitation leaves Initial (manual answer, CANCEL, termination)
- optional logger surfaces previously-swallowed failures

API/robustness:
- unify the duplicated request/Notification parsing paths on a shared core
- holdModifier inserts a=sendonly when SDP has no direction attribute;
  resumeModifier exported and its sendonly caveat documented
- remove console.* debug output from library code
- remove unused BroadSoftOptions and undocumented answerafter alias

Docs and housekeeping:
- README: document Event: hold, mute semantics, NOTIFY response ownership;
  remove non-existent autoApply option; fix non-compiling examples
- drop "prepare" script; untrack compiled test .js and gitignore them

Tests: +36 (regression pins for the Session/Invitation changes, malformed
delays, cancel/races, applyTalkAction, handleRemoteControlNotify contract,
SDP round-trips). Full suite: 5540 passed, 0 failed.
…alog roles on hold re-INVITE

Once the client sends the in-dialog re-INVITE that Event: hold asks for
on a server-originated call, FreeSWITCH (sofia-sip) takes the remote
party from the request's To header - its own identity - and every
subsequent server-generated in-dialog request carries To == From. The
resume (Event: talk) NOTIFY then matches no dialog and was rejected
with 481, leaving the call stuck on hold.

When the full dialog id lookup fails, fall back to matching NOTIFY with
Event: talk/hold by Call-ID plus remote (From) tag, accepting only an
unambiguous single match. RFC 3261 section 12.2.2 explicitly permits
accepting requests whose To tag does not match. All other requests and
events keep the strict matching and the 481.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants