ringcentral-softphone 1.1.7 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -5
- package/dist/cjs/call-session/inbound.js +1 -1
- package/dist/cjs/call-session/index.js +2 -2
- package/dist/cjs/index.js +4 -5
- package/dist/cjs/sip-message/sip-message.d.ts +1 -0
- package/dist/cjs/sip-message/sip-message.js +6 -0
- package/dist/esm/call-session/inbound.js +1 -1
- package/dist/esm/call-session/index.js +2 -2
- package/dist/esm/index.js +4 -5
- package/dist/esm/sip-message/sip-message.d.ts +1 -0
- package/dist/esm/sip-message/sip-message.js +6 -0
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -6,6 +6,9 @@ the
|
|
|
6
6
|
|
|
7
7
|
Users are recommended to use this SDK instead of the JavaScript SDK.
|
|
8
8
|
|
|
9
|
+
This SDK allows you to create a softphone without GUI that runs on server-side
|
|
10
|
+
without a web browser.
|
|
11
|
+
|
|
9
12
|
## Installation
|
|
10
13
|
|
|
11
14
|
```
|
|
@@ -19,19 +22,39 @@ yarn install ringcentral-softphone
|
|
|
19
22
|
1. Login to https://service.ringcentral.com
|
|
20
23
|
2. Find the user/extension you want to use
|
|
21
24
|
3. Check the user's "Devices & Numbers"
|
|
22
|
-
4. Find a phone/device that you want to use
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
4. Find a phone/device that you want to use (Phone type **must** be "Existing
|
|
26
|
+
Phone"), if there is none, you need to create one.
|
|
27
|
+
5. Click the "Set Up and Provision" button
|
|
28
|
+
6. Click the link "Set up manually using SIP"
|
|
29
|
+
7. You will find "SIP Domain", "Outbound Proxy", "User Name", "Password" and
|
|
27
30
|
"Authorization ID"
|
|
28
31
|
|
|
29
32
|
Please note that, "SIP Domain" name should come without port number. I don't
|
|
30
33
|
know why it shows a port number on the page. This SDK requires a "domain" which
|
|
31
34
|
is "SIP Domain" but without the port number.
|
|
32
35
|
|
|
36
|
+
Please also note that, not every device/phone can be used with the softphone
|
|
37
|
+
SDK. Some phones/devices with type "RingCentral Phone app" cannot be used with
|
|
38
|
+
the softphone SDK. You will need to have a device/phone with type **"Exsting
|
|
39
|
+
Phone"**.
|
|
40
|
+
|
|
33
41
|
### Programmatically
|
|
34
42
|
|
|
43
|
+
Invoke this API to list all devices under an extension:
|
|
44
|
+
https://developers.ringcentral.com/api-reference/Devices/listExtensionDevices
|
|
45
|
+
|
|
46
|
+
Please note that, not every device can be used for this softphone SDK. You will
|
|
47
|
+
need to find an device with **`type: 'OtherPhone'`**. Devices with
|
|
48
|
+
`type: 'SoftPhone'` can **NOT** be used for this softphone SDK.
|
|
49
|
+
|
|
50
|
+
I know this is confusing. `type: 'SoftPhone'` in API response is the same as
|
|
51
|
+
`type = "RingCentral Phone app"` in the GUI (mentioned in the Manually section
|
|
52
|
+
above). `type: 'OtherPhone'` in API response is the same as
|
|
53
|
+
`type = "Exiting Phone"` in the GUI.
|
|
54
|
+
|
|
55
|
+
If you cannot find an appropriate device, you will need to create a device
|
|
56
|
+
manually. Please refer to the previous section.
|
|
57
|
+
|
|
35
58
|
Invoke this RESTful API:
|
|
36
59
|
https://developers.ringcentral.com/api-reference/Devices/readDeviceSipInfo
|
|
37
60
|
|
|
@@ -246,6 +269,18 @@ However, for inbound calls, the server doesn't tell us anything about the
|
|
|
246
269
|
Telephony Session ID. Here is a workaround solution:
|
|
247
270
|
https://github.com/tylerlong/rc-softphone-call-id-test
|
|
248
271
|
|
|
272
|
+
## Troubleshooting (Common issues)
|
|
273
|
+
|
|
274
|
+
### `SIP/2.0 486 Busy Here` for outbound call
|
|
275
|
+
|
|
276
|
+
First of all, make sure that the target number is valid. If the target number is
|
|
277
|
+
invalid, you will get `SIP/2.0 486 Busy Here`.
|
|
278
|
+
|
|
279
|
+
Secondly, make sure that the device has a "Emergency Address" configured and
|
|
280
|
+
there is no complains about Emergency address by checking the details of the
|
|
281
|
+
device on https://service.ringcentral.com. It is an known issue that, if the
|
|
282
|
+
Emergency Address is not configured properly, outbound call will not work.
|
|
283
|
+
|
|
249
284
|
---
|
|
250
285
|
|
|
251
286
|
## Dev Notes
|
|
@@ -29,7 +29,7 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${utils_js_1.localKey}
|
|
|
29
29
|
`.trim();
|
|
30
30
|
const newMessage = new index_js_2.OutboundMessage("SIP/2.0 200 OK", {
|
|
31
31
|
Via: this.sipMessage.headers.Via,
|
|
32
|
-
"Call-ID": this.sipMessage.
|
|
32
|
+
"Call-ID": this.sipMessage.getHeader("Call-ID"),
|
|
33
33
|
From: this.sipMessage.headers.From,
|
|
34
34
|
To: this.sipMessage.headers.To,
|
|
35
35
|
CSeq: this.sipMessage.headers.CSeq,
|
|
@@ -50,7 +50,7 @@ class CallSession extends node_events_1.default {
|
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
get callId() {
|
|
53
|
-
return this.sipMessage.
|
|
53
|
+
return this.sipMessage.getHeader("Call-ID");
|
|
54
54
|
}
|
|
55
55
|
send(data) {
|
|
56
56
|
this.socket.send(data, this.remotePort, this.remoteIP);
|
|
@@ -143,7 +143,7 @@ class CallSession extends node_events_1.default {
|
|
|
143
143
|
// send a message to remote server so that it knows where to reply
|
|
144
144
|
this.send("hello");
|
|
145
145
|
const byeHandler = (inboundMessage) => {
|
|
146
|
-
if (inboundMessage.
|
|
146
|
+
if (inboundMessage.getHeader("Call-ID") !== this.callId) {
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
if (inboundMessage.headers.CSeq.endsWith(" BYE")) {
|
package/dist/cjs/index.js
CHANGED
|
@@ -80,8 +80,7 @@ class Softphone extends node_events_1.default {
|
|
|
80
80
|
// sometimes the server will return 200 OK directly
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
-
const wwwAuth = inboundMessage.
|
|
84
|
-
inboundMessage.headers["WWW-Authenticate"];
|
|
83
|
+
const wwwAuth = inboundMessage.getHeader("Www-Authenticate");
|
|
85
84
|
const nonce = wwwAuth.match(/, nonce="(.+?)"/)[1];
|
|
86
85
|
const newMessage = requestMessage.fork();
|
|
87
86
|
newMessage.headers.Authorization = (0, utils_js_1.generateAuthorization)(this.sipInfo, nonce, "REGISTER");
|
|
@@ -90,14 +89,14 @@ class Softphone extends node_events_1.default {
|
|
|
90
89
|
await sipRegister();
|
|
91
90
|
this.intervalHandle = setInterval(() => {
|
|
92
91
|
sipRegister();
|
|
93
|
-
},
|
|
92
|
+
}, 30 * 1000);
|
|
94
93
|
this.on("message", (inboundMessage) => {
|
|
95
94
|
if (!inboundMessage.subject.startsWith("INVITE sip:")) {
|
|
96
95
|
return;
|
|
97
96
|
}
|
|
98
97
|
const outboundMessage = new index_js_1.OutboundMessage("SIP/2.0 100 Trying", {
|
|
99
98
|
Via: inboundMessage.headers.Via,
|
|
100
|
-
"Call-ID": inboundMessage.
|
|
99
|
+
"Call-ID": inboundMessage.getHeader("Call-ID"),
|
|
101
100
|
From: inboundMessage.headers.From,
|
|
102
101
|
To: inboundMessage.headers.To,
|
|
103
102
|
CSeq: inboundMessage.headers.CSeq,
|
|
@@ -181,7 +180,7 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${utils_js_1.localKey}
|
|
|
181
180
|
"Content-Type": "application/sdp",
|
|
182
181
|
}, offerSDP);
|
|
183
182
|
const inboundMessage = await this.send(inviteMessage, true);
|
|
184
|
-
const proxyAuthenticate = inboundMessage.
|
|
183
|
+
const proxyAuthenticate = inboundMessage.getHeader("Proxy-Authenticate");
|
|
185
184
|
const nonce = proxyAuthenticate.match(/, nonce="(.+?)"/)[1];
|
|
186
185
|
const newMessage = inviteMessage.fork();
|
|
187
186
|
newMessage.headers["Proxy-Authorization"] = (0, utils_js_1.generateAuthorization)(this.sipInfo, nonce, "INVITE");
|
|
@@ -24,5 +24,11 @@ class SipMessage {
|
|
|
24
24
|
].join("\r\n");
|
|
25
25
|
return r;
|
|
26
26
|
}
|
|
27
|
+
getHeader(key) {
|
|
28
|
+
const foundKey = Object.keys(this.headers).find((k) => k.toLowerCase() === key.toLowerCase());
|
|
29
|
+
if (foundKey) {
|
|
30
|
+
return this.headers[foundKey];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
27
33
|
}
|
|
28
34
|
exports.default = SipMessage;
|
|
@@ -24,7 +24,7 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}
|
|
|
24
24
|
`.trim();
|
|
25
25
|
const newMessage = new OutboundMessage("SIP/2.0 200 OK", {
|
|
26
26
|
Via: this.sipMessage.headers.Via,
|
|
27
|
-
"Call-ID": this.sipMessage.
|
|
27
|
+
"Call-ID": this.sipMessage.getHeader("Call-ID"),
|
|
28
28
|
From: this.sipMessage.headers.From,
|
|
29
29
|
To: this.sipMessage.headers.To,
|
|
30
30
|
CSeq: this.sipMessage.headers.CSeq,
|
|
@@ -45,7 +45,7 @@ class CallSession extends EventEmitter {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
get callId() {
|
|
48
|
-
return this.sipMessage.
|
|
48
|
+
return this.sipMessage.getHeader("Call-ID");
|
|
49
49
|
}
|
|
50
50
|
send(data) {
|
|
51
51
|
this.socket.send(data, this.remotePort, this.remoteIP);
|
|
@@ -138,7 +138,7 @@ class CallSession extends EventEmitter {
|
|
|
138
138
|
// send a message to remote server so that it knows where to reply
|
|
139
139
|
this.send("hello");
|
|
140
140
|
const byeHandler = (inboundMessage) => {
|
|
141
|
-
if (inboundMessage.
|
|
141
|
+
if (inboundMessage.getHeader("Call-ID") !== this.callId) {
|
|
142
142
|
return;
|
|
143
143
|
}
|
|
144
144
|
if (inboundMessage.headers.CSeq.endsWith(" BYE")) {
|
package/dist/esm/index.js
CHANGED
|
@@ -75,8 +75,7 @@ class Softphone extends EventEmitter {
|
|
|
75
75
|
// sometimes the server will return 200 OK directly
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
|
-
const wwwAuth = inboundMessage.
|
|
79
|
-
inboundMessage.headers["WWW-Authenticate"];
|
|
78
|
+
const wwwAuth = inboundMessage.getHeader("Www-Authenticate");
|
|
80
79
|
const nonce = wwwAuth.match(/, nonce="(.+?)"/)[1];
|
|
81
80
|
const newMessage = requestMessage.fork();
|
|
82
81
|
newMessage.headers.Authorization = generateAuthorization(this.sipInfo, nonce, "REGISTER");
|
|
@@ -85,14 +84,14 @@ class Softphone extends EventEmitter {
|
|
|
85
84
|
await sipRegister();
|
|
86
85
|
this.intervalHandle = setInterval(() => {
|
|
87
86
|
sipRegister();
|
|
88
|
-
},
|
|
87
|
+
}, 30 * 1000);
|
|
89
88
|
this.on("message", (inboundMessage) => {
|
|
90
89
|
if (!inboundMessage.subject.startsWith("INVITE sip:")) {
|
|
91
90
|
return;
|
|
92
91
|
}
|
|
93
92
|
const outboundMessage = new OutboundMessage("SIP/2.0 100 Trying", {
|
|
94
93
|
Via: inboundMessage.headers.Via,
|
|
95
|
-
"Call-ID": inboundMessage.
|
|
94
|
+
"Call-ID": inboundMessage.getHeader("Call-ID"),
|
|
96
95
|
From: inboundMessage.headers.From,
|
|
97
96
|
To: inboundMessage.headers.To,
|
|
98
97
|
CSeq: inboundMessage.headers.CSeq,
|
|
@@ -176,7 +175,7 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}
|
|
|
176
175
|
"Content-Type": "application/sdp",
|
|
177
176
|
}, offerSDP);
|
|
178
177
|
const inboundMessage = await this.send(inviteMessage, true);
|
|
179
|
-
const proxyAuthenticate = inboundMessage.
|
|
178
|
+
const proxyAuthenticate = inboundMessage.getHeader("Proxy-Authenticate");
|
|
180
179
|
const nonce = proxyAuthenticate.match(/, nonce="(.+?)"/)[1];
|
|
181
180
|
const newMessage = inviteMessage.fork();
|
|
182
181
|
newMessage.headers["Proxy-Authorization"] = generateAuthorization(this.sipInfo, nonce, "INVITE");
|
|
@@ -22,5 +22,11 @@ class SipMessage {
|
|
|
22
22
|
].join("\r\n");
|
|
23
23
|
return r;
|
|
24
24
|
}
|
|
25
|
+
getHeader(key) {
|
|
26
|
+
const foundKey = Object.keys(this.headers).find((k) => k.toLowerCase() === key.toLowerCase());
|
|
27
|
+
if (foundKey) {
|
|
28
|
+
return this.headers[foundKey];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
25
31
|
}
|
|
26
32
|
export default SipMessage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ringcentral-softphone",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.9",
|
|
4
4
|
"homepage": "https://github.com/ringcentral/ringcentral-softphone-ts",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"types": "dist/esm/index.d.ts",
|
|
@@ -34,10 +34,11 @@
|
|
|
34
34
|
"werift-rtp": "^0.8.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^22.
|
|
37
|
+
"@types/node": "^22.15.18",
|
|
38
38
|
"dotenv-override-true": "^6.2.2",
|
|
39
|
-
"tsx": "^4.19.
|
|
40
|
-
"typescript": "^5.8.
|
|
39
|
+
"tsx": "^4.19.4",
|
|
40
|
+
"typescript": "^5.8.3",
|
|
41
41
|
"yarn-upgrade-all": "^0.7.5"
|
|
42
|
-
}
|
|
42
|
+
},
|
|
43
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
43
44
|
}
|