ringcentral-softphone 1.2.5 → 1.3.1

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.
@@ -11,7 +11,10 @@ class InboundCallSession extends index_js_1.default {
11
11
  super(softphone, inviteMessage);
12
12
  this.localPeer = inviteMessage.headers.To;
13
13
  this.remotePeer = inviteMessage.headers.From;
14
- this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
14
+ // inbound call from call queue, invite message may not have body
15
+ if (inviteMessage.body.length > 0) {
16
+ this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
17
+ }
15
18
  }
16
19
  async answer() {
17
20
  const answerSDP = `
@@ -41,7 +44,13 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${utils_js_1.localKey}
41
44
  Require: "timer",
42
45
  "Content-Type": "application/sdp",
43
46
  }, answerSDP);
44
- await this.softphone.send(newMessage);
47
+ const ackMessage = await this.softphone.send(newMessage, true);
48
+ // for inbound call from call queue, ack message may HAVE body (while invite message has no body)
49
+ if (ackMessage.body.length > 0) {
50
+ this.remoteIP = ackMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
51
+ this.remotePort = parseInt(ackMessage.body.match(/m=audio (\d+) /)[1], 10);
52
+ this.remoteKey = ackMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
53
+ }
45
54
  this.startLocalServices();
46
55
  }
47
56
  }
@@ -35,8 +35,11 @@ class CallSession extends node_events_1.default {
35
35
  this.encoder = softphone.codec.createEncoder();
36
36
  this.decoder = softphone.codec.createDecoder();
37
37
  this.sipMessage = sipMessage;
38
- this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
39
- this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
38
+ // inbound call from call queue, invite message may not have body
39
+ if (this.sipMessage.body.length > 0) {
40
+ this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
41
+ this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
42
+ }
40
43
  }
41
44
  set remoteKey(key) {
42
45
  const localKeyBuffer = node_buffer_1.Buffer.from(utils_js_1.localKey, "base64");
package/dist/cjs/index.js CHANGED
@@ -143,7 +143,9 @@ class Softphone extends node_events_1.default {
143
143
  }
144
144
  return new Promise((resolve) => {
145
145
  const messageListerner = (inboundMessage) => {
146
- if (inboundMessage.headers.CSeq !== message.headers.CSeq) {
146
+ // "12563 INVITE" vs "12563 ACK"
147
+ if (inboundMessage.headers.CSeq.trim().split(/\s+/)[0] !==
148
+ message.headers.CSeq.trim().split(/\s+/)[0]) {
147
149
  return;
148
150
  }
149
151
  if (inboundMessage.subject.startsWith("SIP/2.0 100 ")) {
@@ -9,10 +9,15 @@ class ResponseMessage extends index_js_1.default {
9
9
  constructor(inboundMessage, responseCode, headers = {}, body = "") {
10
10
  super(undefined, { ...headers }, body);
11
11
  this.subject = `SIP/2.0 ${responseCode} ${response_codes_js_1.default[responseCode]}`;
12
- const keys = ["Via", "From", "To", "Call-ID", "CSeq"];
13
- for (const key of keys) {
14
- if (inboundMessage.headers[key]) {
15
- this.headers[key] = inboundMessage.headers[key];
12
+ const requiredKeys = new Set(["via", "from", "to", "call-id", "cseq"]);
13
+ const allKeys = Object.keys(inboundMessage.headers).reduce((acc, key) => {
14
+ acc[key.toLowerCase()] = key;
15
+ return acc;
16
+ }, {});
17
+ for (const key of requiredKeys) {
18
+ if (allKeys[key]) {
19
+ const originalKey = allKeys[key];
20
+ this.headers[originalKey] = inboundMessage.headers[originalKey];
16
21
  }
17
22
  }
18
23
  }
@@ -6,7 +6,10 @@ class InboundCallSession extends CallSession {
6
6
  super(softphone, inviteMessage);
7
7
  this.localPeer = inviteMessage.headers.To;
8
8
  this.remotePeer = inviteMessage.headers.From;
9
- this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
9
+ // inbound call from call queue, invite message may not have body
10
+ if (inviteMessage.body.length > 0) {
11
+ this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
12
+ }
10
13
  }
11
14
  async answer() {
12
15
  const answerSDP = `
@@ -36,7 +39,13 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}
36
39
  Require: "timer",
37
40
  "Content-Type": "application/sdp",
38
41
  }, answerSDP);
39
- await this.softphone.send(newMessage);
42
+ const ackMessage = await this.softphone.send(newMessage, true);
43
+ // for inbound call from call queue, ack message may HAVE body (while invite message has no body)
44
+ if (ackMessage.body.length > 0) {
45
+ this.remoteIP = ackMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
46
+ this.remotePort = parseInt(ackMessage.body.match(/m=audio (\d+) /)[1], 10);
47
+ this.remoteKey = ackMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
48
+ }
40
49
  this.startLocalServices();
41
50
  }
42
51
  }
@@ -30,8 +30,11 @@ class CallSession extends EventEmitter {
30
30
  this.encoder = softphone.codec.createEncoder();
31
31
  this.decoder = softphone.codec.createDecoder();
32
32
  this.sipMessage = sipMessage;
33
- this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
34
- this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
33
+ // inbound call from call queue, invite message may not have body
34
+ if (this.sipMessage.body.length > 0) {
35
+ this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
36
+ this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
37
+ }
35
38
  }
36
39
  set remoteKey(key) {
37
40
  const localKeyBuffer = Buffer.from(localKey, "base64");
package/dist/esm/index.js CHANGED
@@ -138,7 +138,9 @@ class Softphone extends EventEmitter {
138
138
  }
139
139
  return new Promise((resolve) => {
140
140
  const messageListerner = (inboundMessage) => {
141
- if (inboundMessage.headers.CSeq !== message.headers.CSeq) {
141
+ // "12563 INVITE" vs "12563 ACK"
142
+ if (inboundMessage.headers.CSeq.trim().split(/\s+/)[0] !==
143
+ message.headers.CSeq.trim().split(/\s+/)[0]) {
142
144
  return;
143
145
  }
144
146
  if (inboundMessage.subject.startsWith("SIP/2.0 100 ")) {
@@ -4,10 +4,15 @@ class ResponseMessage extends OutboundMessage {
4
4
  constructor(inboundMessage, responseCode, headers = {}, body = "") {
5
5
  super(undefined, { ...headers }, body);
6
6
  this.subject = `SIP/2.0 ${responseCode} ${responseCodes[responseCode]}`;
7
- const keys = ["Via", "From", "To", "Call-ID", "CSeq"];
8
- for (const key of keys) {
9
- if (inboundMessage.headers[key]) {
10
- this.headers[key] = inboundMessage.headers[key];
7
+ const requiredKeys = new Set(["via", "from", "to", "call-id", "cseq"]);
8
+ const allKeys = Object.keys(inboundMessage.headers).reduce((acc, key) => {
9
+ acc[key.toLowerCase()] = key;
10
+ return acc;
11
+ }, {});
12
+ for (const key of requiredKeys) {
13
+ if (allKeys[key]) {
14
+ const originalKey = allKeys[key];
15
+ this.headers[originalKey] = inboundMessage.headers[originalKey];
11
16
  }
12
17
  }
13
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ringcentral-softphone",
3
- "version": "1.2.5",
3
+ "version": "1.3.1",
4
4
  "homepage": "https://github.com/ringcentral/ringcentral-softphone-ts",
5
5
  "license": "MIT",
6
6
  "types": "dist/esm/index.d.ts",
@@ -25,6 +25,7 @@
25
25
  "in": "rm -rf *.wav && tsx -r dotenv-override-true/config demos/inbound-call.ts",
26
26
  "out": "rm -rf *.wav && tsx -r dotenv-override-true/config demos/outbound-call.ts",
27
27
  "join": "rm -rf *.wav && tsx -r dotenv-override-true/config demos/join-rcv-meeting.ts",
28
+ "multi": "tsx -r dotenv-override-true/config demos/multiple-calls-sequentially.ts",
28
29
  "build": "tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json",
29
30
  "prepublishOnly": "yarn build",
30
31
  "postpublish": "rm -rf dist"
@@ -36,7 +37,7 @@
36
37
  "werift-rtp": "^0.8.8"
37
38
  },
38
39
  "devDependencies": {
39
- "@types/node": "^25.0.3",
40
+ "@types/node": "^25.2.0",
40
41
  "dotenv-override-true": "^6.2.2",
41
42
  "tsx": "^4.21.0",
42
43
  "typescript": "^5.9.3",