fix: session reset after call end, add reject/decline for incoming calls, extract caller ID from SIP headers (P-Asserted-Identity, Remote-Party-ID)

This commit is contained in:
2026-03-17 21:31:53 +05:30
parent 36b3a5d34d
commit aff383cb6d
3 changed files with 76 additions and 14 deletions

View File

@@ -92,6 +92,7 @@ export const CallWidget = () => {
isOnHold,
callDuration,
answer,
reject,
hangup,
toggleMute,
toggleHold,
@@ -171,7 +172,7 @@ export const CallWidget = () => {
<Button size="md" color="primary" iconLeading={Phone01} onClick={answer}>
Answer
</Button>
<Button size="md" color="primary-destructive" iconLeading={PhoneX} onClick={hangup}>
<Button size="md" color="primary-destructive" iconLeading={PhoneX} onClick={reject}>
Decline
</Button>
</div>

View File

@@ -110,6 +110,10 @@ export const useSipPhone = (config?: Partial<SIPConfig>) => {
sipClientRef.current?.answer();
}, []);
const reject = useCallback(() => {
sipClientRef.current?.reject();
}, []);
const hangup = useCallback(() => {
sipClientRef.current?.hangup();
}, []);
@@ -159,6 +163,7 @@ export const useSipPhone = (config?: Partial<SIPConfig>) => {
disconnect,
makeCall,
answer,
reject,
hangup,
toggleMute,
toggleHold,

View File

@@ -15,13 +15,9 @@ export class SIPClient {
) {}
connect(): void {
// Enable JsSIP debug logging to diagnose connection issues
JsSIP.debug.enable('JsSIP:*');
const socket = new JsSIP.WebSocketInterface(this.config.wsServer);
// Extract SIP ID from URI for authorization_user
// URI format: sip:521814@blr-pub-rtc4.ozonetel.com
const sipId = this.config.uri.replace('sip:', '').split('@')[0];
const configuration: UAConfiguration = {
@@ -61,13 +57,19 @@ export class SIPClient {
this.ua.on('newRTCSession', (data: RTCSessionEvent) => {
const session = data.session;
// If we already have an active session, reject the new one
if (this.currentSession && this.currentSession !== session) {
session.terminate();
return;
}
this.currentSession = session;
// Extract caller number
const remoteUri = session.remote_identity?.uri?.toString() ?? '';
const callerNumber = remoteUri.replace('sip:', '').split('@')[0] || 'Unknown';
// Extract caller number — try multiple SIP headers
const callerNumber = this.extractCallerNumber(session);
// Setup audio
// Setup audio for this session
session.on('peerconnection', (e: PeerConnectionEvent) => {
const pc = e.peerconnection;
pc.ontrack = (event: RTCTrackEvent) => {
@@ -95,15 +97,13 @@ export class SIPClient {
}) as CallListener);
session.on('failed', (_e: EndEvent) => {
this.resetSession();
this.onCallStateChange('failed');
this.currentSession = null;
this.cleanupAudio();
});
session.on('ended', (_e: EndEvent) => {
this.resetSession();
this.onCallStateChange('ended');
this.currentSession = null;
this.cleanupAudio();
});
if (session.direction === 'incoming') {
@@ -158,10 +158,22 @@ export class SIPClient {
}
}
reject(): void {
if (this.currentSession && this.currentSession.direction === 'incoming') {
// Use 486 Busy Here for rejecting incoming calls
this.currentSession.terminate({
status_code: 486,
reason_phrase: 'Busy Here',
});
this.resetSession();
this.onCallStateChange('ended');
}
}
hangup(): void {
if (this.currentSession) {
this.currentSession.terminate();
this.currentSession = null;
this.resetSession();
}
}
@@ -197,6 +209,11 @@ export class SIPClient {
return this.ua?.isRegistered() ?? false;
}
private resetSession(): void {
this.currentSession = null;
this.cleanupAudio();
}
private cleanupAudio(): void {
if (this.audioElement) {
this.audioElement.srcObject = null;
@@ -205,6 +222,45 @@ export class SIPClient {
}
}
private extractCallerNumber(session: RTCSession): string {
// Try P-Asserted-Identity header first (most reliable for real caller ID)
try {
const request = session.direction === 'incoming' ? (session as any)._request : null;
if (request) {
// Check P-Asserted-Identity
const pai = request.getHeader('P-Asserted-Identity');
if (pai) {
const match = pai.match(/sip:(\+?\d+)@/);
if (match) return match[1];
}
// Check Remote-Party-ID
const rpid = request.getHeader('Remote-Party-ID');
if (rpid) {
const match = rpid.match(/sip:(\+?\d+)@/);
if (match) return match[1];
}
// Check X-Original-CallerID (custom header some PBX systems use)
const xCid = request.getHeader('X-Original-CallerID');
if (xCid) return xCid;
// Check From header display name (sometimes contains the real number)
const fromDisplay = request.from?.display_name;
if (fromDisplay && /^\+?\d{7,}$/.test(fromDisplay)) {
return fromDisplay;
}
}
} catch (e) {
console.warn('Error extracting caller ID from SIP headers:', e);
}
// Fallback to remote_identity URI
const remoteUri = session.remote_identity?.uri?.toString() ?? '';
const number = remoteUri.replace('sip:', '').split('@')[0] || 'Unknown';
return number;
}
private parseStunServers(stunConfig: string): RTCIceServer[] {
const servers: RTCIceServer[] = [];
const lines = stunConfig.split('\n').filter((line) => line.trim());