callkit
SIP-based Web Phone Call Toolkit
``bash`
pnpm add @koi-design/callkit
This package requires the following peer dependencies:
`json`
{
"dependencies": {
"axios": "~0.26.1",
"blueimp-md5": "^2.12.0",
"sip.js": "^0.21.2"
}
}
`html`
`js
import { CallKit } from '@koi-design/callkit';
const callKit = new CallKit({
host: 'https://example.com',
log: 'debug',
audioRef: () => document.querySelector('#audioRef'),
socket: 'wss://example.com/ws/in-call'
});
`
`js
const { CallKit } = require('@koi-design/callkit');
const callKit = new CallKit({
host: 'https://example.com',
log: 'debug',
audioRef: () => document.querySelector('#audioRef'),
socket: 'wss://example.com/ws/in-call'
});
`
`ts
interface CallKitConfig {
// Request host address
host: string;
// Audio element object
audioRef: HTMLAudioElement | (() => HTMLAudioElement);
// WebSocket address
socket: string;
// WebRTC constraint configuration
constrains?: WebrtcConstranis;
// Log level
log?: 'info' | 'success' | 'warn' | 'error' | 'silent';
// Reconnection configuration
reconnect?: {
// SIP reconnection configuration
sip: {
enabled: boolean; // Enable SIP reconnection (default: true)
maxAttempts: number; // Maximum reconnection attempts (default: 5)
delay: number; // Reconnection delay in ms (default: 3000)
// In-call socket reconnection configuration
incall: {
enabled: boolean; // Enable in-call reconnection (default: true)
maxAttempts: number; // Maximum reconnection attempts (default: 5)
delay: number; // Reconnection delay in ms (default: 3000)
pingInterval: number; // Heartbeat interval in ms (default: 30000)
pingTimeout: number; // Heartbeat timeout in ms (default: 10000)
};
};
// Track logs configuration
trackLogs?: {
enabled: boolean; // Enable log tracking (default: false)
interval: number; // Log upload interval in ms (default: 10000)
maxSize: number; // Maximum log buffer size (default: 100)
};
}
interface WebrtcConstranis {
audio: {
autoGainControl?: boolean;
noiseSuppression?: boolean;
echoCancellation?: boolean;
};
video: false;
}
`
`ts
interface CallKit {
// User login
login(
username: string,
password: string,
extra?: {
encryptionMethod?: 'INTERNAL' | 'NONE';
[key: string]: any;
}
): Promise
// User logout
logout(options?: { isReset?: boolean }): Promise
// Initiate call
call(
extno: string | number,
options?: {
sourceType?: number;
workOrderId?: string;
}
): Promise
// Transfer call
refer(uri: string, options?: any): Promise
// SIP registration
register(): Promise
// SIP unregistration
unregister(): Promise
// Hang up call
hangup(): Promise
// Call hold
hold(): void;
// Release hold
unhold(): void;
// Mute microphone
mute(): void;
// Unmute microphone
unmute(): void;
// Set user status (manually)
setUserStatus(status: number): Promise
// Reset all states
reset(config?: { force?: boolean }): Promise
// Event listener
on(event: string, callback: Function): void;
// Remove event listener
off(event: string, callback?: Function): void;
// Remove all event listeners
removeAllListeners(): void;
}
`
#### Status Change Events
- loginChange - Login status change (param: boolean)registerChange
- - Registration status change (param: boolean)callStatusChange
- - Call status change (param: CallStatus)callIdChange
- - Call ID change (param: string)holdChange
- - Hold status change (param: boolean)muteChange
- - Mute status change (param: boolean)
#### Call Events
- invite - Received incoming call invitation{ accept: Function, reject: Function, getInviteData: Function }
- Callback params: outgoingInvite
- - Received outgoing call invitationconnecting
- - Call connecting (param: timestamp)ringing
- - Remote party ringing (param: timestamp)pickUp
- - Remote party answered (param: timestamp)agentPickUp
- - Agent connected (param: timestamp)hangUp
- - Remote party hung up (param: timestamp)noAnswer
- - No answer (param: timestamp)callEnd
- - Call ended (param: timestamp)callCdr
- - Call detail record push (param: CDR data)
#### Connection Events
- IncallConnectEvent - In-call socket connection eventsipConnectEvent
- - SIP connection eventsipRegistererEvent
- - SIP registerer eventsipSessionEvent
- - SIP session event
#### System Events
- log - System log eventerror
- - Error occurred (param: { code: number, message: string })socketEvent
- - Forward all socket events (param: socket message)
`js`
const CallStatus = {
init: 0, // Initial state/hung up
connecting: 2, // Connecting
ringing: 4, // Ringing
calling: 5 // In call
};
> Note: In version 2.0.0+, registered and holding statuses have been removed. Registration status is now tracked separately via the registerChange event.
> Note: In version 2.0.0+, user status management has been removed from CallKit. You should maintain user status through your own application using the setUserStatus method.
`js
// Example user status codes (define as needed in your application)
const UserStatus = {
offline: 1, // Offline
online: 2, // Online & Free
catNap: 3, // Cat Nap
busy: 4, // Busy
training: 5, // Training
processing: 6 // Processing
};
// Set user status manually
await callKit.setUserStatus(UserStatus.online);
`
`js
const ErrorCode = {
// Unknown error
UNKNOWN_ERROR: -1,
// API errors (1000xxx)
API_USER_LOGIN_ERROR: 1000001, // User login failed
API_USER_STATUS_UPDATE_ERROR: 1000002, // Update user status failed
API_USER_LOGOUT_ERROR: 1000003, // User logout failed
// WebRTC errors (2000xxx)
CONNECT_CALL_STATUS_ERROR: 2000001, // Call status exception
USER_NOT_LOGIN: 2000002, // User not logged in
WEBRTC_USER_MEDIA_ERROR: 2000003, // WebRTC not supported
WEBRTC_HOLE_STATUS_ERROR: 2000004, // Call hold failed
WEBRTC_AUDIO_PLAYER_ERROR: 2000005, // Audio player does not exist
WEBRTC_AUDIO_PLAY_ERROR: 2000006, // Audio playback failed
WEBRTC_USER_AGENT_ERROR: 2000007, // User agent startup failed
WEBRTC_CALL_INVITE_ERROR: 2000008, // Call request failed
WEBRTC_REGISTER_ERROR: 2000009, // Register request failed
WEBRTC_MUTE_STATUS_ERROR: 2000010, // Mute failed
WEBRTC_CANCEL_REGISTER_ERROR: 2000011, // Unregister failed
WEBRTC_MUTE_ERROR: 2000012, // Mute operation failed
// WebSocket errors (3000xxx)
SOCKET_CONNECT_ERROR: 3000001, // Connection exception
SOCKET_PING_TIMEOUT: 3000002, // Ping timeout
SOKET_SERVER_ERROR: 3000003, // Server exception
SOCKET_CALL_ERROR: 3000004, // Call failed
SOCKET_RECONNECT_FAILED: 3000005 // Reconnection limit exceeded
};
`
`js`
callKit.on('callStatusChange', (status) => {
const statusMap = {
0: 'Initial state',
2: 'Connecting',
4: 'Ringing',
5: 'In call'
};
console.log('Current status:', statusMap[status]);
});
`js`
callKit.on('registerChange', (isRegistered) => {
console.log(
'Registration status:',
isRegistered ? 'Registered' : 'Unregistered'
);
});
`js
let startTime;
callKit.on('pickUp', (date) => {
startTime = date;
});
callKit.on('callEnd', (date) => {
const duration = date - startTime;
console.log('Call duration (ms):', duration);
});
`
`js
callKit.on('invite', ({ accept, reject, getInviteData }) => {
// Get call data
const inviteData = getInviteData();
console.log('Incoming call:', inviteData);
// Accept the call
accept();
// Or reject the call
// reject();
});
`
`js
callKit.on('error', (error) => {
console.error('Error code:', error.code);
console.error('Error message:', error.message);
switch (error.code) {
case 1000001:
// Handle login failure
break;
case 2000002:
// Handle not logged in error
break;
case 3000005:
// Handle reconnection failure
break;
// ...other error handling
}
});
`
`js
// Listen to reconnection events
callKit.on('IncallConnectEvent', (event) => {
console.log('In-call connection event:', event);
});
callKit.on('sipConnectEvent', (event) => {
console.log('SIP connection event:', event);
});
// Configure reconnection options
const callKit = new CallKit({
host: 'https://example.com',
socket: 'wss://example.com/ws/in-call',
audioRef: () => document.querySelector('#audioRef'),
reconnect: {
sip: {
enabled: true,
maxAttempts: 5,
delay: 3000
},
incall: {
enabled: true,
maxAttempts: 5,
delay: 3000
}
}
});
`
`js
// Mute microphone
callKit.mute();
// Unmute microphone
callKit.unmute();
// Listen to mute status changes
callKit.on('muteChange', (isMuted) => {
console.log('Mute status:', isMuted ? 'Muted' : 'Unmuted');
});
`
`js
// Hold call
callKit.hold();
// Unhold call
callKit.unhold();
// Listen to hold status changes
callKit.on('holdChange', (isHeld) => {
console.log('Hold status:', isHeld ? 'On hold' : 'Active');
});
`
For complete TypeScript type definitions, please refer to index.d.ts
1. User Status Management Removed
- CallKit no longer automatically manages user status
- Use setUserStatus() method to manually maintain user status
2. Registration Status Separated
- Registration status is no longer part of call status
- Use registerChange event to track registration status independently
3. Call Status Simplified
- Removed registered (1) status - use registerChange event insteadholding
- Removed (3) status - use holdChange event insteadinit
- Current statuses: (0), connecting (2), ringing (4), calling (5)
4. Enhanced Reconnection Mechanism
- Completely refactored SIP and socket reconnection logic
- Added configurable reconnection options for both SIP and in-call connections
- New error code: SOCKET_RECONNECT_FAILED (3000005)
5. New Methods
- mute() / unmute() - Control microphone mutingrefer(uri, options)
- - Transfer callsoff(event, callback)
- - Remove specific event listenersremoveAllListeners()
- - Remove all event listenerssetUserStatus(status)
- - Manually set user status
6. New Events
- callIdChange - Call ID change eventoutgoingInvite
- - Outgoing call invitation eventhangUp
- - Remote party hang up eventIncallConnectEvent
- - In-call socket connection eventsipConnectEvent
- - SIP connection eventsipRegistererEvent
- - SIP registerer eventsipSessionEvent` - SIP session event
-
Please refer to MIGRATION.md for detailed migration instructions from v1.x to v2.0.
MIT