ringcentral-softphone 1.3.7 → 1.3.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.
Files changed (39) hide show
  1. package/README.md +31 -4
  2. package/dist/call-session/inbound.cjs +3 -1
  3. package/dist/call-session/inbound.d.cts +1 -1
  4. package/dist/call-session/inbound.d.mts +1 -1
  5. package/dist/call-session/inbound.mjs +4 -2
  6. package/dist/call-session/inbound.mjs.map +1 -1
  7. package/dist/call-session/index.cjs +21 -2
  8. package/dist/call-session/index.d.cts +1 -1
  9. package/dist/call-session/index.d.mts +1 -1
  10. package/dist/call-session/index.mjs +21 -2
  11. package/dist/call-session/index.mjs.map +1 -1
  12. package/dist/call-session/outbound.cjs +2 -1
  13. package/dist/call-session/outbound.d.cts +1 -1
  14. package/dist/call-session/outbound.d.mts +1 -1
  15. package/dist/call-session/outbound.mjs +2 -1
  16. package/dist/call-session/outbound.mjs.map +1 -1
  17. package/dist/call-session/streamer.d.cts +1 -1
  18. package/dist/call-session/streamer.d.mts +1 -1
  19. package/dist/call-session/streamer.mjs.map +1 -1
  20. package/dist/codec.mjs.map +1 -1
  21. package/dist/dtmf.mjs.map +1 -1
  22. package/dist/{inbound--wGoGqLS.d.mts → inbound-BhY2IDed.d.mts} +10 -3
  23. package/dist/inbound-BhY2IDed.d.mts.map +1 -0
  24. package/dist/{inbound-DquvTSj1.d.cts → inbound-NTxLsDeV.d.cts} +11 -4
  25. package/dist/inbound-NTxLsDeV.d.cts.map +1 -0
  26. package/dist/index.cjs +20 -9
  27. package/dist/index.d.cts +1 -1
  28. package/dist/index.d.mts +1 -1
  29. package/dist/index.mjs +21 -10
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/sip-message/inbound/index.mjs.map +1 -1
  32. package/dist/sip-message/outbound/index.mjs.map +1 -1
  33. package/dist/sip-message/outbound/request.mjs.map +1 -1
  34. package/dist/sip-message/outbound/response.mjs.map +1 -1
  35. package/dist/sip-message/sip-message.mjs.map +1 -1
  36. package/dist/utils.mjs.map +1 -1
  37. package/package.json +10 -6
  38. package/dist/inbound--wGoGqLS.d.mts.map +0 -1
  39. package/dist/inbound-DquvTSj1.d.cts.map +0 -1
package/README.md CHANGED
@@ -9,13 +9,14 @@ Users are recommended to use this SDK instead of the JavaScript SDK.
9
9
  This SDK allows you to create a softphone without GUI that runs on server-side
10
10
  without a web browser.
11
11
 
12
- ## New documentation and new name
12
+ ## More documentation
13
13
 
14
- New documentation is available here:
14
+ More documentation is available here:
15
15
  https://ringcentral.github.io/ringcentral-softphone-ts/
16
16
 
17
- We are renaming this SDK to **RingCentral Cloud Phone SDK**, and it is currently
18
- a work in progress.
17
+ But the documentation is not very up-to-date. And it mentions another name of this SDK: **RingCentral Cloud Phone SDK**
18
+
19
+ It's just for your information. Latest documentation is always this README file.
19
20
 
20
21
  ## Installation
21
22
 
@@ -127,6 +128,32 @@ await softphone.register();
127
128
 
128
129
  For complete examples, see [demos/](demos/)
129
130
 
131
+ ## E2E test (real credentials)
132
+
133
+ This repository includes one real integration test under `tests/e2e/`.
134
+
135
+ Put two SIP accounts in `.env`:
136
+
137
+ ```bash
138
+ SIP_A_DOMAIN=sip.ringcentral.com
139
+ SIP_A_OUTBOUND_PROXY=sip10.ringcentral.com:5096
140
+ SIP_A_USERNAME=1650...
141
+ SIP_A_PASSWORD=...
142
+ SIP_A_AUTHORIZATION_ID=...
143
+
144
+ SIP_B_DOMAIN=sip.ringcentral.com
145
+ SIP_B_OUTBOUND_PROXY=sip10.ringcentral.com:5096
146
+ SIP_B_USERNAME=1650...
147
+ SIP_B_PASSWORD=...
148
+ SIP_B_AUTHORIZATION_ID=...
149
+ ```
150
+
151
+ Run:
152
+
153
+ ```bash
154
+ yarn test
155
+ ```
156
+
130
157
  ## Debug mode
131
158
 
132
159
  ```ts
@@ -11,13 +11,15 @@ var InboundCallSession = class extends require_call_session_index {
11
11
  if (inviteMessage.body.length > 0) this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
12
12
  }
13
13
  async answer() {
14
+ const { socket, port } = await require_call_session_index.createBoundSocket();
15
+ this.socket = socket;
14
16
  const answerSDP = `
15
17
  v=0
16
18
  o=- ${Date.now()} 0 IN IP4 ${this.softphone.client.localAddress}
17
19
  s=rc-softphone-ts
18
20
  c=IN IP4 ${this.softphone.client.localAddress}
19
21
  t=0 0
20
- m=audio ${require_utils.randomInt()} RTP/SAVP ${this.softphone.codec.id} 101
22
+ m=audio ${port} RTP/SAVP ${this.softphone.codec.id} 101
21
23
  a=rtpmap:${this.softphone.codec.id} ${this.softphone.codec.name}
22
24
  a=rtpmap:101 telephone-event/8000
23
25
  a=fmtp:101 0-15
@@ -1,2 +1,2 @@
1
- import { t as InboundCallSession } from "../inbound-DquvTSj1.cjs";
1
+ import { t as InboundCallSession } from "../inbound-NTxLsDeV.cjs";
2
2
  export = InboundCallSession;
@@ -1,2 +1,2 @@
1
- import { t as InboundCallSession } from "../inbound--wGoGqLS.mjs";
1
+ import { t as InboundCallSession } from "../inbound-BhY2IDed.mjs";
2
2
  export { InboundCallSession as default };
@@ -1,4 +1,4 @@
1
- import { localKey, randomInt } from "../utils.mjs";
1
+ import { localKey } from "../utils.mjs";
2
2
  import OutboundMessage from "../sip-message/outbound/index.mjs";
3
3
  import "../sip-message/index.mjs";
4
4
  import CallSession from "./index.mjs";
@@ -11,13 +11,15 @@ var InboundCallSession = class extends CallSession {
11
11
  if (inviteMessage.body.length > 0) this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
12
12
  }
13
13
  async answer() {
14
+ const { socket, port } = await CallSession.createBoundSocket();
15
+ this.socket = socket;
14
16
  const answerSDP = `
15
17
  v=0
16
18
  o=- ${Date.now()} 0 IN IP4 ${this.softphone.client.localAddress}
17
19
  s=rc-softphone-ts
18
20
  c=IN IP4 ${this.softphone.client.localAddress}
19
21
  t=0 0
20
- m=audio ${randomInt()} RTP/SAVP ${this.softphone.codec.id} 101
22
+ m=audio ${port} RTP/SAVP ${this.softphone.codec.id} 101
21
23
  a=rtpmap:${this.softphone.codec.id} ${this.softphone.codec.name}
22
24
  a=rtpmap:101 telephone-event/8000
23
25
  a=fmtp:101 0-15
@@ -1 +1 @@
1
- {"version":3,"file":"inbound.mjs","names":[],"sources":["../../src/call-session/inbound.ts"],"sourcesContent":["import type Softphone from \"../index.js\";\nimport { type InboundMessage, OutboundMessage } from \"../sip-message/index.js\";\nimport { localKey, randomInt } from \"../utils.js\";\nimport CallSession from \"./index.js\";\n\nclass InboundCallSession extends CallSession {\n public constructor(softphone: Softphone, inviteMessage: InboundMessage) {\n super(softphone, inviteMessage);\n this.localPeer = inviteMessage.headers.To;\n this.remotePeer = inviteMessage.headers.From;\n // inbound call from call queue, invite message may not have body\n if (inviteMessage.body.length > 0) {\n this.remoteKey = inviteMessage.body.match(\n /AES_CM_128_HMAC_SHA1_80 inline:([\\w+/]+)/,\n )![1];\n }\n }\n\n public async answer() {\n const answerSDP = `\nv=0\no=- ${Date.now()} 0 IN IP4 ${this.softphone.client.localAddress}\ns=rc-softphone-ts\nc=IN IP4 ${this.softphone.client.localAddress}\nt=0 0\nm=audio ${randomInt()} RTP/SAVP ${this.softphone.codec.id} 101\na=rtpmap:${this.softphone.codec.id} ${this.softphone.codec.name}\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-15\na=sendrecv\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}\n`.trim();\n this.sdp = answerSDP;\n const newMessage = new OutboundMessage(\n \"SIP/2.0 200 OK\",\n {\n Via: this.sipMessage.headers.Via,\n \"Call-ID\": this.sipMessage.getHeader(\"Call-ID\"),\n From: this.sipMessage.headers.From,\n To: this.sipMessage.headers.To,\n CSeq: this.sipMessage.headers.CSeq,\n Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,\n Allow:\n \"PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS\",\n Supported: \"replaces, 100rel, timer, norefersub\",\n \"Session-Expires\": \"14400;refresher=uac\",\n Require: \"timer\",\n \"Content-Type\": \"application/sdp\",\n },\n answerSDP,\n );\n const ackMessage = await this.softphone.send(newMessage, true);\n\n // for inbound call from call queue, ack message may HAVE body (while invite message has no body)\n if (ackMessage.body.length > 0) {\n this.remoteIP = ackMessage.body.match(/c=IN IP4 ([\\d.]+)/)![1];\n this.remotePort = parseInt(\n ackMessage.body.match(/m=audio (\\d+) /)![1],\n 10,\n );\n this.remoteKey = ackMessage.body.match(\n /AES_CM_128_HMAC_SHA1_80 inline:([\\w+/]+)/,\n )![1];\n }\n\n this.startLocalServices();\n }\n}\n\nexport default InboundCallSession;\n"],"mappings":";;;;;AAKA,IAAM,qBAAN,cAAiC,YAAY;CAC3C,YAAmB,WAAsB,eAA+B;AACtE,QAAM,WAAW,cAAc;AAC/B,OAAK,YAAY,cAAc,QAAQ;AACvC,OAAK,aAAa,cAAc,QAAQ;AAExC,MAAI,cAAc,KAAK,SAAS,EAC9B,MAAK,YAAY,cAAc,KAAK,MAClC,2CACD,CAAE;;CAIP,MAAa,SAAS;EACpB,MAAM,YAAY;;MAEhB,KAAK,KAAK,CAAC,YAAY,KAAK,UAAU,OAAO,aAAa;;WAErD,KAAK,UAAU,OAAO,aAAa;;UAEpC,WAAW,CAAC,YAAY,KAAK,UAAU,MAAM,GAAG;WAC/C,KAAK,UAAU,MAAM,GAAG,GAAG,KAAK,UAAU,MAAM,KAAK;;;;4CAIpB,SAAS;EACnD,MAAM;AACJ,OAAK,MAAM;EACX,MAAM,aAAa,IAAI,gBACrB,kBACA;GACE,KAAK,KAAK,WAAW,QAAQ;GAC7B,WAAW,KAAK,WAAW,UAAU,UAAU;GAC/C,MAAM,KAAK,WAAW,QAAQ;GAC9B,IAAI,KAAK,WAAW,QAAQ;GAC5B,MAAM,KAAK,WAAW,QAAQ;GAC9B,SAAS,QAAQ,KAAK,UAAU,QAAQ,SAAS,GAAG,KAAK,UAAU,OAAO,aAAa,GAAG,KAAK,UAAU,OAAO,UAAU;GAC1H,OACE;GACF,WAAW;GACX,mBAAmB;GACnB,SAAS;GACT,gBAAgB;GACjB,EACD,UACD;EACD,MAAM,aAAa,MAAM,KAAK,UAAU,KAAK,YAAY,KAAK;AAG9D,MAAI,WAAW,KAAK,SAAS,GAAG;AAC9B,QAAK,WAAW,WAAW,KAAK,MAAM,oBAAoB,CAAE;AAC5D,QAAK,aAAa,SAChB,WAAW,KAAK,MAAM,iBAAiB,CAAE,IACzC,GACD;AACD,QAAK,YAAY,WAAW,KAAK,MAC/B,2CACD,CAAE;;AAGL,OAAK,oBAAoB"}
1
+ {"version":3,"file":"inbound.mjs","names":[],"sources":["../../src/call-session/inbound.ts"],"sourcesContent":["import type Softphone from \"../index.js\";\nimport { type InboundMessage, OutboundMessage } from \"../sip-message/index.js\";\nimport { localKey } from \"../utils.js\";\nimport CallSession from \"./index.js\";\n\nclass InboundCallSession extends CallSession {\n public constructor(softphone: Softphone, inviteMessage: InboundMessage) {\n super(softphone, inviteMessage);\n this.localPeer = inviteMessage.headers.To;\n this.remotePeer = inviteMessage.headers.From;\n // inbound call from call queue, invite message may not have body\n if (inviteMessage.body.length > 0) {\n this.remoteKey = inviteMessage.body.match(\n /AES_CM_128_HMAC_SHA1_80 inline:([\\w+/]+)/,\n )![1];\n }\n }\n\n public async answer() {\n const { socket, port } = await CallSession.createBoundSocket();\n this.socket = socket;\n const answerSDP = `\nv=0\no=- ${Date.now()} 0 IN IP4 ${this.softphone.client.localAddress}\ns=rc-softphone-ts\nc=IN IP4 ${this.softphone.client.localAddress}\nt=0 0\nm=audio ${port} RTP/SAVP ${this.softphone.codec.id} 101\na=rtpmap:${this.softphone.codec.id} ${this.softphone.codec.name}\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-15\na=sendrecv\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}\n`.trim();\n this.sdp = answerSDP;\n const newMessage = new OutboundMessage(\n \"SIP/2.0 200 OK\",\n {\n Via: this.sipMessage.headers.Via,\n \"Call-ID\": this.sipMessage.getHeader(\"Call-ID\"),\n From: this.sipMessage.headers.From,\n To: this.sipMessage.headers.To,\n CSeq: this.sipMessage.headers.CSeq,\n Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,\n Allow:\n \"PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS\",\n Supported: \"replaces, 100rel, timer, norefersub\",\n \"Session-Expires\": \"14400;refresher=uac\",\n Require: \"timer\",\n \"Content-Type\": \"application/sdp\",\n },\n answerSDP,\n );\n const ackMessage = await this.softphone.send(newMessage, true);\n\n // for inbound call from call queue, ack message may HAVE body (while invite message has no body)\n if (ackMessage.body.length > 0) {\n this.remoteIP = ackMessage.body.match(/c=IN IP4 ([\\d.]+)/)![1];\n this.remotePort = parseInt(\n ackMessage.body.match(/m=audio (\\d+) /)![1],\n 10,\n );\n this.remoteKey = ackMessage.body.match(\n /AES_CM_128_HMAC_SHA1_80 inline:([\\w+/]+)/,\n )![1];\n }\n\n this.startLocalServices();\n }\n}\n\nexport default InboundCallSession;\n"],"mappings":";;;;;AAKA,IAAM,qBAAN,cAAiC,YAAY;CAC3C,YAAmB,WAAsB,eAA+B;EACtE,MAAM,WAAW,cAAc;EAC/B,KAAK,YAAY,cAAc,QAAQ;EACvC,KAAK,aAAa,cAAc,QAAQ;EAExC,IAAI,cAAc,KAAK,SAAS,GAC9B,KAAK,YAAY,cAAc,KAAK,MAClC,2CACD,CAAE;;CAIP,MAAa,SAAS;EACpB,MAAM,EAAE,QAAQ,SAAS,MAAM,YAAY,mBAAmB;EAC9D,KAAK,SAAS;EACd,MAAM,YAAY;;MAEhB,KAAK,KAAK,CAAC,YAAY,KAAK,UAAU,OAAO,aAAa;;WAErD,KAAK,UAAU,OAAO,aAAa;;UAEpC,KAAK,YAAY,KAAK,UAAU,MAAM,GAAG;WACxC,KAAK,UAAU,MAAM,GAAG,GAAG,KAAK,UAAU,MAAM,KAAK;;;;4CAIpB,SAAS;EACnD,MAAM;EACJ,KAAK,MAAM;EACX,MAAM,aAAa,IAAI,gBACrB,kBACA;GACE,KAAK,KAAK,WAAW,QAAQ;GAC7B,WAAW,KAAK,WAAW,UAAU,UAAU;GAC/C,MAAM,KAAK,WAAW,QAAQ;GAC9B,IAAI,KAAK,WAAW,QAAQ;GAC5B,MAAM,KAAK,WAAW,QAAQ;GAC9B,SAAS,QAAQ,KAAK,UAAU,QAAQ,SAAS,GAAG,KAAK,UAAU,OAAO,aAAa,GAAG,KAAK,UAAU,OAAO,UAAU;GAC1H,OACE;GACF,WAAW;GACX,mBAAmB;GACnB,SAAS;GACT,gBAAgB;GACjB,EACD,UACD;EACD,MAAM,aAAa,MAAM,KAAK,UAAU,KAAK,YAAY,KAAK;EAG9D,IAAI,WAAW,KAAK,SAAS,GAAG;GAC9B,KAAK,WAAW,WAAW,KAAK,MAAM,oBAAoB,CAAE;GAC5D,KAAK,aAAa,SAChB,WAAW,KAAK,MAAM,iBAAiB,CAAE,IACzC,GACD;GACD,KAAK,YAAY,WAAW,KAAK,MAC/B,2CACD,CAAE;;EAGL,KAAK,oBAAoB"}
@@ -42,6 +42,26 @@ var CallSession = class extends node_events.default {
42
42
  this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
43
43
  }
44
44
  }
45
+ static async createBoundSocket() {
46
+ const socket = node_dgram.default.createSocket("udp4");
47
+ return await new Promise((resolve, reject) => {
48
+ const onError = (error) => {
49
+ socket.removeListener("listening", onListening);
50
+ socket.close();
51
+ reject(error);
52
+ };
53
+ const onListening = () => {
54
+ socket.removeListener("error", onError);
55
+ resolve({
56
+ socket,
57
+ port: socket.address().port
58
+ });
59
+ };
60
+ socket.once("error", onError);
61
+ socket.once("listening", onListening);
62
+ socket.bind(0);
63
+ });
64
+ }
45
65
  set remoteKey(key) {
46
66
  const localKeyBuffer = node_buffer.Buffer.from(require_utils.localKey, "base64");
47
67
  const remoteKeyBuffer = node_buffer.Buffer.from(key, "base64");
@@ -115,7 +135,7 @@ var CallSession = class extends node_events.default {
115
135
  this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
116
136
  }
117
137
  startLocalServices() {
118
- this.socket = node_dgram.default.createSocket("udp4");
138
+ if (!this.socket) throw new Error("RTP socket is not initialized; expected pre-bound socket from SDP setup");
119
139
  this.socket.on("message", (message) => {
120
140
  const rtpPacket = werift_rtp.RtpPacket.deSerialize(this.srtpSession.decrypt(message));
121
141
  this.emit("rtpPacket", rtpPacket);
@@ -133,7 +153,6 @@ var CallSession = class extends node_events.default {
133
153
  }
134
154
  }
135
155
  });
136
- this.socket.bind();
137
156
  this.send("hello");
138
157
  const byeHandler = (inboundMessage) => {
139
158
  if (inboundMessage.getHeader("Call-ID") !== this.callId) return;
@@ -1,2 +1,2 @@
1
- import { i as CallSession } from "../inbound-DquvTSj1.cjs";
1
+ import { i as CallSession } from "../inbound-NTxLsDeV.cjs";
2
2
  export = CallSession;
@@ -1,2 +1,2 @@
1
- import { i as CallSession } from "../inbound--wGoGqLS.mjs";
1
+ import { i as CallSession } from "../inbound-BhY2IDed.mjs";
2
2
  export { CallSession as default };
@@ -38,6 +38,26 @@ var CallSession = class extends EventEmitter {
38
38
  this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
39
39
  }
40
40
  }
41
+ static async createBoundSocket() {
42
+ const socket = dgram.createSocket("udp4");
43
+ return await new Promise((resolve, reject) => {
44
+ const onError = (error) => {
45
+ socket.removeListener("listening", onListening);
46
+ socket.close();
47
+ reject(error);
48
+ };
49
+ const onListening = () => {
50
+ socket.removeListener("error", onError);
51
+ resolve({
52
+ socket,
53
+ port: socket.address().port
54
+ });
55
+ };
56
+ socket.once("error", onError);
57
+ socket.once("listening", onListening);
58
+ socket.bind(0);
59
+ });
60
+ }
41
61
  set remoteKey(key) {
42
62
  const localKeyBuffer = Buffer.from(localKey, "base64");
43
63
  const remoteKeyBuffer = Buffer.from(key, "base64");
@@ -111,7 +131,7 @@ var CallSession = class extends EventEmitter {
111
131
  this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
112
132
  }
113
133
  startLocalServices() {
114
- this.socket = dgram.createSocket("udp4");
134
+ if (!this.socket) throw new Error("RTP socket is not initialized; expected pre-bound socket from SDP setup");
115
135
  this.socket.on("message", (message) => {
116
136
  const rtpPacket = RtpPacket.deSerialize(this.srtpSession.decrypt(message));
117
137
  this.emit("rtpPacket", rtpPacket);
@@ -129,7 +149,6 @@ var CallSession = class extends EventEmitter {
129
149
  }
130
150
  }
131
151
  });
132
- this.socket.bind();
133
152
  this.send("hello");
134
153
  const byeHandler = (inboundMessage) => {
135
154
  if (inboundMessage.getHeader("Call-ID") !== this.callId) return;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/call-session/index.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\nimport dgram from \"node:dgram\";\nimport EventEmitter from \"node:events\";\nimport waitFor from \"wait-for-async\";\nimport { RtpHeader, RtpPacket, SrtpSession } from \"werift-rtp\";\nimport DTMF from \"../dtmf.js\";\nimport type Softphone from \"../index.js\";\nimport {\n type InboundMessage,\n RequestMessage,\n ResponseMessage,\n} from \"../sip-message/index.js\";\nimport { branch, extractAddress, localKey, randomInt } from \"../utils.js\";\nimport Streamer from \"./streamer.js\";\n\ntype DtmfChar = (typeof DTMF.phoneChars)[number];\n\nconst isDtmfChar = (value: string): value is DtmfChar =>\n (DTMF.phoneChars as readonly string[]).includes(value);\n\nabstract class CallSession extends EventEmitter {\n public softphone: Softphone;\n public sipMessage: InboundMessage;\n public socket!: dgram.Socket;\n public localPeer!: string;\n public remotePeer!: string;\n public remoteIP!: string;\n public remotePort!: number;\n public disposed = false;\n public srtpSession!: SrtpSession;\n public encoder: { encode: (pcm: Buffer) => Buffer };\n public decoder: { decode: (audio: Buffer) => Buffer };\n public sdp!: string;\n\n // for audio streaming\n public ssrc = randomInt();\n public sequenceNumber = randomInt();\n public timestamp = randomInt();\n\n public constructor(softphone: Softphone, sipMessage: InboundMessage) {\n super();\n this.softphone = softphone;\n this.encoder = softphone.codec.createEncoder();\n this.decoder = softphone.codec.createDecoder();\n this.sipMessage = sipMessage;\n // inbound call from call queue, invite message may not have body\n if (this.sipMessage.body.length > 0) {\n this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\\d.]+)/)![1];\n this.remotePort = parseInt(\n this.sipMessage.body.match(/m=audio (\\d+) /)![1],\n 10,\n );\n }\n }\n\n public set remoteKey(key: string) {\n const localKeyBuffer = Buffer.from(localKey, \"base64\");\n const remoteKeyBuffer = Buffer.from(key, \"base64\");\n this.srtpSession = new SrtpSession({\n profile: 0x0001,\n keys: {\n localMasterKey: localKeyBuffer.subarray(0, 16),\n localMasterSalt: localKeyBuffer.subarray(16, 30),\n remoteMasterKey: remoteKeyBuffer.subarray(0, 16),\n remoteMasterSalt: remoteKeyBuffer.subarray(16, 30),\n },\n });\n }\n\n public get callId() {\n return this.sipMessage.getHeader(\"Call-ID\");\n }\n\n public send(data: string | Buffer) {\n this.socket.send(data, this.remotePort, this.remoteIP);\n }\n\n public async hangup() {\n const requestMessage = new RequestMessage(\n `BYE sip:${this.softphone.sipInfo.domain} SIP/2.0`,\n {\n \"Call-ID\": this.callId,\n From: this.localPeer,\n To: this.remotePeer,\n Via: `SIP/2.0/TLS ${this.softphone.fakeDomain};branch=${branch()}`,\n },\n );\n await this.softphone.send(requestMessage);\n }\n\n public sendDTMF(char: DtmfChar) {\n const payloads = DTMF.charToPayloads(char);\n const timestamp = this.timestamp;\n let first = true;\n for (const payload of payloads) {\n const rtpHeader = new RtpHeader({\n version: 2,\n padding: false,\n paddingSize: 0,\n extension: false,\n marker: first,\n payloadOffset: 12,\n payloadType: 101,\n sequenceNumber: this.sequenceNumber,\n timestamp,\n ssrc: this.ssrc,\n csrcLength: 0,\n csrc: [],\n extensionProfile: 48862,\n extensionLength: undefined,\n extensions: [],\n });\n const rtpPacket = new RtpPacket(rtpHeader, payload);\n this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));\n this.sequenceNumber = (this.sequenceNumber + 1) % 65536;\n first = false;\n }\n this.timestamp += 800;\n }\n\n public async sendDTMFs(s: string, delay = 500) {\n for (const c of s) {\n if (!isDtmfChar(c)) {\n throw new Error(`invalid phone char: ${c}`);\n }\n this.sendDTMF(c);\n await waitFor({ interval: delay });\n }\n }\n\n // buffer is the content of a audio file, it is supposed to be uncompressed PCM data\n // The audio should be playable by command: play -t raw -b 16 -r 16000 -e signed-integer test.wav\n public streamAudio(input: Buffer) {\n const streamer = new Streamer(this, input);\n streamer.start();\n return streamer;\n }\n\n // send a single rtp packet\n public sendPacket(rtpPacket: RtpPacket) {\n if (this.disposed) {\n return;\n }\n this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));\n }\n\n protected startLocalServices() {\n this.socket = dgram.createSocket(\"udp4\");\n this.socket.on(\"message\", (message) => {\n const rtpPacket = RtpPacket.deSerialize(\n this.srtpSession.decrypt(message),\n );\n this.emit(\"rtpPacket\", rtpPacket);\n if (rtpPacket.header.payloadType === 101) {\n this.emit(\"dtmfPacket\", rtpPacket);\n const char = DTMF.payloadToChar(rtpPacket.payload);\n if (char) {\n this.emit(\"dtmf\", char);\n }\n } else if (rtpPacket.header.payloadType === this.softphone.codec.id) {\n if (\n rtpPacket.payload.length === 4 &&\n rtpPacket.payload[0] >= 0x00 &&\n rtpPacket.payload[0] < 0x0c &&\n rtpPacket.payload[1] === 0x8a &&\n rtpPacket.payload[2] === 0x03 &&\n rtpPacket.payload[3] === 0xc0\n ) {\n // special DTMF packet in audio format\n // first byte 0x00 to 0x0c means DTMF 0 to 9, *, #\n // we ignore it since DTMF is handled by `if (rtpPacket.header.payloadType === 101) {`\n return; // ignore it\n }\n try {\n rtpPacket.payload = this.decoder.decode(rtpPacket.payload);\n this.emit(\"audioPacket\", rtpPacket);\n } catch {\n console.error(\"Audio packet decode failed\", rtpPacket);\n }\n }\n });\n\n // as I tested, we can use a random port here and it still works\n // but it seems that in SDP we need to tell remote our local IP Address, not 127.0.0.1\n this.socket.bind(); // random port\n // send a message to remote server so that it knows where to reply\n this.send(\"hello\");\n\n const byeHandler = (inboundMessage: InboundMessage) => {\n if (inboundMessage.getHeader(\"Call-ID\") !== this.callId) {\n return;\n }\n if (inboundMessage.headers.CSeq.endsWith(\" BYE\")) {\n this.softphone.off(\"message\", byeHandler);\n this.dispose();\n }\n };\n this.softphone.on(\"message\", byeHandler);\n }\n\n protected dispose() {\n this.disposed = true;\n this.emit(\"disposed\");\n this.removeAllListeners();\n this.socket?.removeAllListeners();\n this.socket?.close();\n }\n\n public async transfer(transferTo: string) {\n const requestMessage = new RequestMessage(\n `REFER sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.outboundProxy};transport=tls SIP/2.0`,\n {\n Via: `SIP/2.0/TLS ${this.softphone.client.localAddress}:${this.softphone.client.localPort};rport;branch=${branch()};alias`,\n \"Max-Forwards\": 70,\n From: this.localPeer,\n To: this.remotePeer,\n Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,\n \"Call-ID\": this.callId,\n Event: \"refer\",\n Expires: 600,\n Supported: \"replaces, 100rel, timer, norefersub\",\n Accept: \"message/sipfrag;version=2.0\",\n \"Allow-Events\": \"presence, message-summary, refer\",\n \"Refer-To\": `sip:${transferTo}@${this.softphone.sipInfo.domain}`,\n \"Referred-By\": `<sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.domain}>`,\n },\n );\n await this.softphone.send(requestMessage);\n\n return new Promise<void>((resolve) => {\n const notifyHandler = (inboundMessage: InboundMessage) => {\n if (!inboundMessage.subject.startsWith(\"NOTIFY \")) {\n return;\n }\n const responseMessage = new ResponseMessage(inboundMessage, 200);\n this.softphone.send(responseMessage);\n if (inboundMessage.body.trim() === \"SIP/2.0 200 OK\") {\n this.softphone.off(\"message\", notifyHandler);\n resolve();\n }\n };\n this.softphone.on(\"message\", notifyHandler);\n });\n }\n\n public async toggleReceive(toReceive: boolean) {\n let newSDP = this.sdp;\n if (!toReceive) {\n newSDP = newSDP.replace(/a=sendrecv/, \"a=sendonly\");\n }\n const requestMessage = new RequestMessage(\n `INVITE ${extractAddress(this.remotePeer)} SIP/2.0`,\n {\n \"Call-Id\": this.callId,\n From: this.localPeer,\n To: this.remotePeer,\n Via: `SIP/2.0/TLS ${this.softphone.client.localAddress}:${this.softphone.client.localPort};rport;branch=${branch()};alias`,\n \"Content-Type\": \"application/sdp\",\n Contact: ` <sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,\n },\n newSDP,\n );\n const replyMessage = await this.softphone.send(requestMessage, true);\n const ackMessage = new RequestMessage(\n `ACK ${extractAddress(this.remotePeer)} SIP/2.0`,\n {\n \"Call-Id\": this.callId,\n From: this.localPeer,\n To: this.remotePeer,\n Via: replyMessage.headers.Via,\n CSeq: replyMessage.headers.CSeq.replace(\" INVITE\", \" ACK\"),\n },\n );\n await this.softphone.send(ackMessage);\n }\n\n public async hold() {\n return this.toggleReceive(false);\n }\n\n public async unhold() {\n return this.toggleReceive(true);\n }\n}\n\nexport default CallSession;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/call-session/index.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\nimport dgram from \"node:dgram\";\nimport EventEmitter from \"node:events\";\nimport waitFor from \"wait-for-async\";\nimport { RtpHeader, RtpPacket, SrtpSession } from \"werift-rtp\";\nimport DTMF from \"../dtmf.js\";\nimport type Softphone from \"../index.js\";\nimport {\n type InboundMessage,\n RequestMessage,\n ResponseMessage,\n} from \"../sip-message/index.js\";\nimport { branch, extractAddress, localKey, randomInt } from \"../utils.js\";\nimport Streamer from \"./streamer.js\";\n\ntype DtmfChar = (typeof DTMF.phoneChars)[number];\n\nconst isDtmfChar = (value: string): value is DtmfChar =>\n (DTMF.phoneChars as readonly string[]).includes(value);\n\nabstract class CallSession extends EventEmitter {\n public softphone: Softphone;\n public sipMessage: InboundMessage;\n public socket!: dgram.Socket;\n public localPeer!: string;\n public remotePeer!: string;\n public remoteIP!: string;\n public remotePort!: number;\n public disposed = false;\n public srtpSession!: SrtpSession;\n public encoder: { encode: (pcm: Buffer) => Buffer };\n public decoder: { decode: (audio: Buffer) => Buffer };\n public sdp!: string;\n\n // for audio streaming\n public ssrc = randomInt();\n public sequenceNumber = randomInt();\n public timestamp = randomInt();\n\n public constructor(softphone: Softphone, sipMessage: InboundMessage) {\n super();\n this.softphone = softphone;\n this.encoder = softphone.codec.createEncoder();\n this.decoder = softphone.codec.createDecoder();\n this.sipMessage = sipMessage;\n // inbound call from call queue, invite message may not have body\n if (this.sipMessage.body.length > 0) {\n this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\\d.]+)/)![1];\n this.remotePort = parseInt(\n this.sipMessage.body.match(/m=audio (\\d+) /)![1],\n 10,\n );\n }\n }\n\n public static async createBoundSocket() {\n const socket = dgram.createSocket(\"udp4\");\n return await new Promise<{ socket: dgram.Socket; port: number }>(\n (resolve, reject) => {\n const onError = (error: Error) => {\n socket.removeListener(\"listening\", onListening);\n socket.close();\n reject(error);\n };\n const onListening = () => {\n socket.removeListener(\"error\", onError);\n const address = socket.address();\n resolve({ socket, port: address.port });\n };\n socket.once(\"error\", onError);\n socket.once(\"listening\", onListening);\n socket.bind(0);\n },\n );\n }\n\n public set remoteKey(key: string) {\n const localKeyBuffer = Buffer.from(localKey, \"base64\");\n const remoteKeyBuffer = Buffer.from(key, \"base64\");\n this.srtpSession = new SrtpSession({\n profile: 0x0001,\n keys: {\n localMasterKey: localKeyBuffer.subarray(0, 16),\n localMasterSalt: localKeyBuffer.subarray(16, 30),\n remoteMasterKey: remoteKeyBuffer.subarray(0, 16),\n remoteMasterSalt: remoteKeyBuffer.subarray(16, 30),\n },\n });\n }\n\n public get callId() {\n return this.sipMessage.getHeader(\"Call-ID\");\n }\n\n public send(data: string | Buffer) {\n this.socket.send(data, this.remotePort, this.remoteIP);\n }\n\n public async hangup() {\n const requestMessage = new RequestMessage(\n `BYE sip:${this.softphone.sipInfo.domain} SIP/2.0`,\n {\n \"Call-ID\": this.callId,\n From: this.localPeer,\n To: this.remotePeer,\n Via: `SIP/2.0/TLS ${this.softphone.fakeDomain};branch=${branch()}`,\n },\n );\n await this.softphone.send(requestMessage);\n }\n\n public sendDTMF(char: DtmfChar) {\n const payloads = DTMF.charToPayloads(char);\n const timestamp = this.timestamp;\n let first = true;\n for (const payload of payloads) {\n const rtpHeader = new RtpHeader({\n version: 2,\n padding: false,\n paddingSize: 0,\n extension: false,\n marker: first,\n payloadOffset: 12,\n payloadType: 101,\n sequenceNumber: this.sequenceNumber,\n timestamp,\n ssrc: this.ssrc,\n csrcLength: 0,\n csrc: [],\n extensionProfile: 48862,\n extensionLength: undefined,\n extensions: [],\n });\n const rtpPacket = new RtpPacket(rtpHeader, payload);\n this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));\n this.sequenceNumber = (this.sequenceNumber + 1) % 65536;\n first = false;\n }\n this.timestamp += 800;\n }\n\n public async sendDTMFs(s: string, delay = 500) {\n for (const c of s) {\n if (!isDtmfChar(c)) {\n throw new Error(`invalid phone char: ${c}`);\n }\n this.sendDTMF(c);\n await waitFor({ interval: delay });\n }\n }\n\n // buffer is the content of a audio file, it is supposed to be uncompressed PCM data\n // The audio should be playable by command: play -t raw -b 16 -r 16000 -e signed-integer test.wav\n public streamAudio(input: Buffer) {\n const streamer = new Streamer(this, input);\n streamer.start();\n return streamer;\n }\n\n // send a single rtp packet\n public sendPacket(rtpPacket: RtpPacket) {\n if (this.disposed) {\n return;\n }\n this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));\n }\n\n protected startLocalServices() {\n if (!this.socket) {\n throw new Error(\n \"RTP socket is not initialized; expected pre-bound socket from SDP setup\",\n );\n }\n this.socket.on(\"message\", (message) => {\n const rtpPacket = RtpPacket.deSerialize(\n this.srtpSession.decrypt(message),\n );\n this.emit(\"rtpPacket\", rtpPacket);\n if (rtpPacket.header.payloadType === 101) {\n this.emit(\"dtmfPacket\", rtpPacket);\n const char = DTMF.payloadToChar(rtpPacket.payload);\n if (char) {\n this.emit(\"dtmf\", char);\n }\n } else if (rtpPacket.header.payloadType === this.softphone.codec.id) {\n if (\n rtpPacket.payload.length === 4 &&\n rtpPacket.payload[0] >= 0x00 &&\n rtpPacket.payload[0] < 0x0c &&\n rtpPacket.payload[1] === 0x8a &&\n rtpPacket.payload[2] === 0x03 &&\n rtpPacket.payload[3] === 0xc0\n ) {\n // special DTMF packet in audio format\n // first byte 0x00 to 0x0c means DTMF 0 to 9, *, #\n // we ignore it since DTMF is handled by `if (rtpPacket.header.payloadType === 101) {`\n return; // ignore it\n }\n try {\n rtpPacket.payload = this.decoder.decode(rtpPacket.payload);\n this.emit(\"audioPacket\", rtpPacket);\n } catch {\n console.error(\"Audio packet decode failed\", rtpPacket);\n }\n }\n });\n\n // send a message to remote server so that it knows where to reply\n this.send(\"hello\");\n\n const byeHandler = (inboundMessage: InboundMessage) => {\n if (inboundMessage.getHeader(\"Call-ID\") !== this.callId) {\n return;\n }\n if (inboundMessage.headers.CSeq.endsWith(\" BYE\")) {\n this.softphone.off(\"message\", byeHandler);\n this.dispose();\n }\n };\n this.softphone.on(\"message\", byeHandler);\n }\n\n protected dispose() {\n this.disposed = true;\n this.emit(\"disposed\");\n this.removeAllListeners();\n this.socket?.removeAllListeners();\n this.socket?.close();\n }\n\n public async transfer(transferTo: string) {\n const requestMessage = new RequestMessage(\n `REFER sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.outboundProxy};transport=tls SIP/2.0`,\n {\n Via: `SIP/2.0/TLS ${this.softphone.client.localAddress}:${this.softphone.client.localPort};rport;branch=${branch()};alias`,\n \"Max-Forwards\": 70,\n From: this.localPeer,\n To: this.remotePeer,\n Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,\n \"Call-ID\": this.callId,\n Event: \"refer\",\n Expires: 600,\n Supported: \"replaces, 100rel, timer, norefersub\",\n Accept: \"message/sipfrag;version=2.0\",\n \"Allow-Events\": \"presence, message-summary, refer\",\n \"Refer-To\": `sip:${transferTo}@${this.softphone.sipInfo.domain}`,\n \"Referred-By\": `<sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.domain}>`,\n },\n );\n await this.softphone.send(requestMessage);\n\n return new Promise<void>((resolve) => {\n const notifyHandler = (inboundMessage: InboundMessage) => {\n if (!inboundMessage.subject.startsWith(\"NOTIFY \")) {\n return;\n }\n const responseMessage = new ResponseMessage(inboundMessage, 200);\n this.softphone.send(responseMessage);\n if (inboundMessage.body.trim() === \"SIP/2.0 200 OK\") {\n this.softphone.off(\"message\", notifyHandler);\n resolve();\n }\n };\n this.softphone.on(\"message\", notifyHandler);\n });\n }\n\n public async toggleReceive(toReceive: boolean) {\n let newSDP = this.sdp;\n if (!toReceive) {\n newSDP = newSDP.replace(/a=sendrecv/, \"a=sendonly\");\n }\n const requestMessage = new RequestMessage(\n `INVITE ${extractAddress(this.remotePeer)} SIP/2.0`,\n {\n \"Call-Id\": this.callId,\n From: this.localPeer,\n To: this.remotePeer,\n Via: `SIP/2.0/TLS ${this.softphone.client.localAddress}:${this.softphone.client.localPort};rport;branch=${branch()};alias`,\n \"Content-Type\": \"application/sdp\",\n Contact: ` <sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,\n },\n newSDP,\n );\n const replyMessage = await this.softphone.send(requestMessage, true);\n const ackMessage = new RequestMessage(\n `ACK ${extractAddress(this.remotePeer)} SIP/2.0`,\n {\n \"Call-Id\": this.callId,\n From: this.localPeer,\n To: this.remotePeer,\n Via: replyMessage.headers.Via,\n CSeq: replyMessage.headers.CSeq.replace(\" INVITE\", \" ACK\"),\n },\n );\n await this.softphone.send(ackMessage);\n }\n\n public async hold() {\n return this.toggleReceive(false);\n }\n\n public async unhold() {\n return this.toggleReceive(true);\n }\n}\n\nexport default CallSession;\n"],"mappings":"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"}
@@ -4,8 +4,9 @@ require("../sip-message/index.cjs");
4
4
  const require_call_session_index = require("./index.cjs");
5
5
  //#region src/call-session/outbound.ts
6
6
  var OutboundCallSession = class extends require_call_session_index {
7
- constructor(softphone, answerMessage) {
7
+ constructor(softphone, answerMessage, socket) {
8
8
  super(softphone, answerMessage);
9
+ this.socket = socket;
9
10
  this.localPeer = answerMessage.headers.From;
10
11
  this.remotePeer = answerMessage.headers.To;
11
12
  this.remoteKey = answerMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
@@ -1,2 +1,2 @@
1
- import { r as OutboundCallSession } from "../inbound-DquvTSj1.cjs";
1
+ import { r as OutboundCallSession } from "../inbound-NTxLsDeV.cjs";
2
2
  export = OutboundCallSession;
@@ -1,2 +1,2 @@
1
- import { r as OutboundCallSession } from "../inbound--wGoGqLS.mjs";
1
+ import { r as OutboundCallSession } from "../inbound-BhY2IDed.mjs";
2
2
  export { OutboundCallSession as default };
@@ -4,8 +4,9 @@ import "../sip-message/index.mjs";
4
4
  import CallSession from "./index.mjs";
5
5
  //#region src/call-session/outbound.ts
6
6
  var OutboundCallSession = class extends CallSession {
7
- constructor(softphone, answerMessage) {
7
+ constructor(softphone, answerMessage, socket) {
8
8
  super(softphone, answerMessage);
9
+ this.socket = socket;
9
10
  this.localPeer = answerMessage.headers.From;
10
11
  this.remotePeer = answerMessage.headers.To;
11
12
  this.remoteKey = answerMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
@@ -1 +1 @@
1
- {"version":3,"file":"outbound.mjs","names":[],"sources":["../../src/call-session/outbound.ts"],"sourcesContent":["import type Softphone from \"../index.js\";\nimport { type InboundMessage, RequestMessage } from \"../sip-message/index.js\";\nimport { extractAddress, withoutTag } from \"../utils.js\";\nimport CallSession from \"./index.js\";\n\nclass OutboundCallSession extends CallSession {\n public constructor(softphone: Softphone, answerMessage: InboundMessage) {\n super(softphone, answerMessage);\n this.localPeer = answerMessage.headers.From;\n this.remotePeer = answerMessage.headers.To;\n this.remoteKey = answerMessage.body.match(\n /AES_CM_128_HMAC_SHA1_80 inline:([\\w+/]+)/,\n )![1];\n this.init();\n }\n\n public init() {\n // wait for user to answer the call\n const answerHandler = (message: InboundMessage) => {\n if (message.headers.CSeq !== this.sipMessage.headers.CSeq) {\n return;\n }\n if (message.subject.startsWith(\"SIP/2.0 486\")) {\n this.softphone.off(\"message\", answerHandler);\n this.emit(\"busy\");\n this.dispose();\n return;\n }\n if (message.subject.startsWith(\"SIP/2.0 200\")) {\n this.softphone.off(\"message\", answerHandler);\n this.emit(\"answered\");\n\n const ackMessage = new RequestMessage(\n `ACK ${extractAddress(this.remotePeer)} SIP/2.0`,\n {\n \"Call-ID\": this.callId,\n From: this.localPeer,\n To: this.remotePeer,\n Via: this.sipMessage.headers.Via,\n CSeq: this.sipMessage.headers.CSeq.replace(\" INVITE\", \" ACK\"),\n },\n );\n this.softphone.send(ackMessage);\n }\n };\n this.softphone.on(\"message\", answerHandler);\n this.once(\"answered\", () => this.startLocalServices());\n }\n\n public async cancel() {\n const requestMessage = new RequestMessage(\n `CANCEL ${extractAddress(this.remotePeer)} SIP/2.0`,\n {\n \"Call-ID\": this.callId,\n From: this.localPeer,\n To: withoutTag(this.remotePeer),\n Via: this.sipMessage.headers.Via,\n CSeq: this.sipMessage.headers.CSeq.replace(\" INVITE\", \" CANCEL\"),\n },\n );\n await this.softphone.send(requestMessage);\n }\n\n public get sessionId() {\n const header = this.sipMessage.headers[\"p-rc-api-ids\"];\n const match = header.match(/party-id=([^;]+);session-id=([^;]+)/)!;\n return match[2];\n }\n public get partyId() {\n const header = this.sipMessage.headers[\"p-rc-api-ids\"];\n const match = header.match(/party-id=([^;]+);session-id=([^;]+)/)!;\n return match[1];\n }\n}\n\nexport default OutboundCallSession;\n"],"mappings":";;;;;AAKA,IAAM,sBAAN,cAAkC,YAAY;CAC5C,YAAmB,WAAsB,eAA+B;AACtE,QAAM,WAAW,cAAc;AAC/B,OAAK,YAAY,cAAc,QAAQ;AACvC,OAAK,aAAa,cAAc,QAAQ;AACxC,OAAK,YAAY,cAAc,KAAK,MAClC,2CACD,CAAE;AACH,OAAK,MAAM;;CAGb,OAAc;EAEZ,MAAM,iBAAiB,YAA4B;AACjD,OAAI,QAAQ,QAAQ,SAAS,KAAK,WAAW,QAAQ,KACnD;AAEF,OAAI,QAAQ,QAAQ,WAAW,cAAc,EAAE;AAC7C,SAAK,UAAU,IAAI,WAAW,cAAc;AAC5C,SAAK,KAAK,OAAO;AACjB,SAAK,SAAS;AACd;;AAEF,OAAI,QAAQ,QAAQ,WAAW,cAAc,EAAE;AAC7C,SAAK,UAAU,IAAI,WAAW,cAAc;AAC5C,SAAK,KAAK,WAAW;IAErB,MAAM,aAAa,IAAI,eACrB,OAAO,eAAe,KAAK,WAAW,CAAC,WACvC;KACE,WAAW,KAAK;KAChB,MAAM,KAAK;KACX,IAAI,KAAK;KACT,KAAK,KAAK,WAAW,QAAQ;KAC7B,MAAM,KAAK,WAAW,QAAQ,KAAK,QAAQ,WAAW,OAAO;KAC9D,CACF;AACD,SAAK,UAAU,KAAK,WAAW;;;AAGnC,OAAK,UAAU,GAAG,WAAW,cAAc;AAC3C,OAAK,KAAK,kBAAkB,KAAK,oBAAoB,CAAC;;CAGxD,MAAa,SAAS;EACpB,MAAM,iBAAiB,IAAI,eACzB,UAAU,eAAe,KAAK,WAAW,CAAC,WAC1C;GACE,WAAW,KAAK;GAChB,MAAM,KAAK;GACX,IAAI,WAAW,KAAK,WAAW;GAC/B,KAAK,KAAK,WAAW,QAAQ;GAC7B,MAAM,KAAK,WAAW,QAAQ,KAAK,QAAQ,WAAW,UAAU;GACjE,CACF;AACD,QAAM,KAAK,UAAU,KAAK,eAAe;;CAG3C,IAAW,YAAY;AAGrB,SAFe,KAAK,WAAW,QAAQ,gBAClB,MAAM,sCACf,CAAC;;CAEf,IAAW,UAAU;AAGnB,SAFe,KAAK,WAAW,QAAQ,gBAClB,MAAM,sCACf,CAAC"}
1
+ {"version":3,"file":"outbound.mjs","names":[],"sources":["../../src/call-session/outbound.ts"],"sourcesContent":["import type dgram from \"node:dgram\";\nimport type Softphone from \"../index.js\";\nimport { type InboundMessage, RequestMessage } from \"../sip-message/index.js\";\nimport { extractAddress, withoutTag } from \"../utils.js\";\nimport CallSession from \"./index.js\";\n\nclass OutboundCallSession extends CallSession {\n public constructor(\n softphone: Softphone,\n answerMessage: InboundMessage,\n socket: dgram.Socket,\n ) {\n super(softphone, answerMessage);\n this.socket = socket;\n this.localPeer = answerMessage.headers.From;\n this.remotePeer = answerMessage.headers.To;\n this.remoteKey = answerMessage.body.match(\n /AES_CM_128_HMAC_SHA1_80 inline:([\\w+/]+)/,\n )![1];\n this.init();\n }\n\n public init() {\n // wait for user to answer the call\n const answerHandler = (message: InboundMessage) => {\n if (message.headers.CSeq !== this.sipMessage.headers.CSeq) {\n return;\n }\n if (message.subject.startsWith(\"SIP/2.0 486\")) {\n this.softphone.off(\"message\", answerHandler);\n this.emit(\"busy\");\n this.dispose();\n return;\n }\n if (message.subject.startsWith(\"SIP/2.0 200\")) {\n this.softphone.off(\"message\", answerHandler);\n this.emit(\"answered\");\n\n const ackMessage = new RequestMessage(\n `ACK ${extractAddress(this.remotePeer)} SIP/2.0`,\n {\n \"Call-ID\": this.callId,\n From: this.localPeer,\n To: this.remotePeer,\n Via: this.sipMessage.headers.Via,\n CSeq: this.sipMessage.headers.CSeq.replace(\" INVITE\", \" ACK\"),\n },\n );\n this.softphone.send(ackMessage);\n }\n };\n this.softphone.on(\"message\", answerHandler);\n this.once(\"answered\", () => this.startLocalServices());\n }\n\n public async cancel() {\n const requestMessage = new RequestMessage(\n `CANCEL ${extractAddress(this.remotePeer)} SIP/2.0`,\n {\n \"Call-ID\": this.callId,\n From: this.localPeer,\n To: withoutTag(this.remotePeer),\n Via: this.sipMessage.headers.Via,\n CSeq: this.sipMessage.headers.CSeq.replace(\" INVITE\", \" CANCEL\"),\n },\n );\n await this.softphone.send(requestMessage);\n }\n\n public get sessionId() {\n const header = this.sipMessage.headers[\"p-rc-api-ids\"];\n const match = header.match(/party-id=([^;]+);session-id=([^;]+)/)!;\n return match[2];\n }\n public get partyId() {\n const header = this.sipMessage.headers[\"p-rc-api-ids\"];\n const match = header.match(/party-id=([^;]+);session-id=([^;]+)/)!;\n return match[1];\n }\n}\n\nexport default OutboundCallSession;\n"],"mappings":";;;;;AAMA,IAAM,sBAAN,cAAkC,YAAY;CAC5C,YACE,WACA,eACA,QACA;EACA,MAAM,WAAW,cAAc;EAC/B,KAAK,SAAS;EACd,KAAK,YAAY,cAAc,QAAQ;EACvC,KAAK,aAAa,cAAc,QAAQ;EACxC,KAAK,YAAY,cAAc,KAAK,MAClC,2CACD,CAAE;EACH,KAAK,MAAM;;CAGb,OAAc;EAEZ,MAAM,iBAAiB,YAA4B;GACjD,IAAI,QAAQ,QAAQ,SAAS,KAAK,WAAW,QAAQ,MACnD;GAEF,IAAI,QAAQ,QAAQ,WAAW,cAAc,EAAE;IAC7C,KAAK,UAAU,IAAI,WAAW,cAAc;IAC5C,KAAK,KAAK,OAAO;IACjB,KAAK,SAAS;IACd;;GAEF,IAAI,QAAQ,QAAQ,WAAW,cAAc,EAAE;IAC7C,KAAK,UAAU,IAAI,WAAW,cAAc;IAC5C,KAAK,KAAK,WAAW;IAErB,MAAM,aAAa,IAAI,eACrB,OAAO,eAAe,KAAK,WAAW,CAAC,WACvC;KACE,WAAW,KAAK;KAChB,MAAM,KAAK;KACX,IAAI,KAAK;KACT,KAAK,KAAK,WAAW,QAAQ;KAC7B,MAAM,KAAK,WAAW,QAAQ,KAAK,QAAQ,WAAW,OAAO;KAC9D,CACF;IACD,KAAK,UAAU,KAAK,WAAW;;;EAGnC,KAAK,UAAU,GAAG,WAAW,cAAc;EAC3C,KAAK,KAAK,kBAAkB,KAAK,oBAAoB,CAAC;;CAGxD,MAAa,SAAS;EACpB,MAAM,iBAAiB,IAAI,eACzB,UAAU,eAAe,KAAK,WAAW,CAAC,WAC1C;GACE,WAAW,KAAK;GAChB,MAAM,KAAK;GACX,IAAI,WAAW,KAAK,WAAW;GAC/B,KAAK,KAAK,WAAW,QAAQ;GAC7B,MAAM,KAAK,WAAW,QAAQ,KAAK,QAAQ,WAAW,UAAU;GACjE,CACF;EACD,MAAM,KAAK,UAAU,KAAK,eAAe;;CAG3C,IAAW,YAAY;EAGrB,OAFe,KAAK,WAAW,QAAQ,gBAClB,MAAM,sCACf,CAAC;;CAEf,IAAW,UAAU;EAGnB,OAFe,KAAK,WAAW,QAAQ,gBAClB,MAAM,sCACf,CAAC"}
@@ -1,2 +1,2 @@
1
- import { a as Streamer } from "../inbound-DquvTSj1.cjs";
1
+ import { a as Streamer } from "../inbound-NTxLsDeV.cjs";
2
2
  export = Streamer;
@@ -1,2 +1,2 @@
1
- import { a as Streamer } from "../inbound--wGoGqLS.mjs";
1
+ import { a as Streamer } from "../inbound-BhY2IDed.mjs";
2
2
  export { Streamer as default };
@@ -1 +1 @@
1
- {"version":3,"file":"streamer.mjs","names":[],"sources":["../../src/call-session/streamer.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\nimport EventEmitter from \"node:events\";\n\nimport { RtpHeader, RtpPacket } from \"werift-rtp\";\n\nimport type CallSession from \"./index.js\";\n\nclass Streamer extends EventEmitter {\n public paused = false;\n private callSession: CallSession;\n private buffer: Buffer;\n private originalBuffer: Buffer;\n\n public constructor(callSession: CallSession, buffer: Buffer) {\n super();\n this.callSession = callSession;\n this.buffer = buffer;\n this.originalBuffer = buffer;\n }\n\n public start() {\n this.buffer = this.originalBuffer;\n this.paused = false;\n this.sendPacket();\n }\n\n public stop() {\n this.buffer = Buffer.alloc(0);\n }\n\n public pause() {\n this.paused = true;\n }\n\n public resume() {\n this.paused = false;\n this.sendPacket();\n }\n\n public get finished() {\n return (\n this.callSession.disposed ||\n this.buffer.length < this.callSession.softphone.codec.packetSize\n );\n }\n\n private sendPacket() {\n if (!this.paused && !this.finished) {\n const temp = this.callSession.encoder.encode(\n this.buffer.subarray(0, this.callSession.softphone.codec.packetSize),\n );\n const rtpPacket = new RtpPacket(\n new RtpHeader({\n version: 2,\n padding: false,\n paddingSize: 0,\n extension: false,\n marker: false,\n payloadOffset: 12,\n payloadType: this.callSession.softphone.codec.id,\n sequenceNumber: this.callSession.sequenceNumber,\n timestamp: this.callSession.timestamp,\n ssrc: this.callSession.ssrc,\n csrcLength: 0,\n csrc: [],\n extensionProfile: 48862,\n extensionLength: undefined,\n extensions: [],\n }),\n temp,\n );\n this.callSession.send(\n this.callSession.srtpSession.encrypt(\n rtpPacket.payload,\n rtpPacket.header,\n ),\n );\n this.callSession.sequenceNumber += 1;\n if (this.callSession.sequenceNumber > 65535) {\n this.callSession.sequenceNumber = 0;\n }\n this.callSession.timestamp +=\n this.callSession.softphone.codec.timestampInterval;\n this.buffer = this.buffer.subarray(\n this.callSession.softphone.codec.packetSize,\n );\n if (this.finished) {\n this.emit(\"finished\");\n } else {\n setTimeout(() => this.sendPacket(), 20);\n }\n }\n }\n}\n\nexport default Streamer;\n"],"mappings":";;;;AAOA,IAAM,WAAN,cAAuB,aAAa;CAClC,SAAgB;CAChB;CACA;CACA;CAEA,YAAmB,aAA0B,QAAgB;AAC3D,SAAO;AACP,OAAK,cAAc;AACnB,OAAK,SAAS;AACd,OAAK,iBAAiB;;CAGxB,QAAe;AACb,OAAK,SAAS,KAAK;AACnB,OAAK,SAAS;AACd,OAAK,YAAY;;CAGnB,OAAc;AACZ,OAAK,SAAS,OAAO,MAAM,EAAE;;CAG/B,QAAe;AACb,OAAK,SAAS;;CAGhB,SAAgB;AACd,OAAK,SAAS;AACd,OAAK,YAAY;;CAGnB,IAAW,WAAW;AACpB,SACE,KAAK,YAAY,YACjB,KAAK,OAAO,SAAS,KAAK,YAAY,UAAU,MAAM;;CAI1D,aAAqB;AACnB,MAAI,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU;GAClC,MAAM,OAAO,KAAK,YAAY,QAAQ,OACpC,KAAK,OAAO,SAAS,GAAG,KAAK,YAAY,UAAU,MAAM,WAAW,CACrE;GACD,MAAM,YAAY,IAAI,UACpB,IAAI,UAAU;IACZ,SAAS;IACT,SAAS;IACT,aAAa;IACb,WAAW;IACX,QAAQ;IACR,eAAe;IACf,aAAa,KAAK,YAAY,UAAU,MAAM;IAC9C,gBAAgB,KAAK,YAAY;IACjC,WAAW,KAAK,YAAY;IAC5B,MAAM,KAAK,YAAY;IACvB,YAAY;IACZ,MAAM,EAAE;IACR,kBAAkB;IAClB,iBAAiB,KAAA;IACjB,YAAY,EAAE;IACf,CAAC,EACF,KACD;AACD,QAAK,YAAY,KACf,KAAK,YAAY,YAAY,QAC3B,UAAU,SACV,UAAU,OACX,CACF;AACD,QAAK,YAAY,kBAAkB;AACnC,OAAI,KAAK,YAAY,iBAAiB,MACpC,MAAK,YAAY,iBAAiB;AAEpC,QAAK,YAAY,aACf,KAAK,YAAY,UAAU,MAAM;AACnC,QAAK,SAAS,KAAK,OAAO,SACxB,KAAK,YAAY,UAAU,MAAM,WAClC;AACD,OAAI,KAAK,SACP,MAAK,KAAK,WAAW;OAErB,kBAAiB,KAAK,YAAY,EAAE,GAAG"}
1
+ {"version":3,"file":"streamer.mjs","names":[],"sources":["../../src/call-session/streamer.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\nimport EventEmitter from \"node:events\";\n\nimport { RtpHeader, RtpPacket } from \"werift-rtp\";\n\nimport type CallSession from \"./index.js\";\n\nclass Streamer extends EventEmitter {\n public paused = false;\n private callSession: CallSession;\n private buffer: Buffer;\n private originalBuffer: Buffer;\n\n public constructor(callSession: CallSession, buffer: Buffer) {\n super();\n this.callSession = callSession;\n this.buffer = buffer;\n this.originalBuffer = buffer;\n }\n\n public start() {\n this.buffer = this.originalBuffer;\n this.paused = false;\n this.sendPacket();\n }\n\n public stop() {\n this.buffer = Buffer.alloc(0);\n }\n\n public pause() {\n this.paused = true;\n }\n\n public resume() {\n this.paused = false;\n this.sendPacket();\n }\n\n public get finished() {\n return (\n this.callSession.disposed ||\n this.buffer.length < this.callSession.softphone.codec.packetSize\n );\n }\n\n private sendPacket() {\n if (!this.paused && !this.finished) {\n const temp = this.callSession.encoder.encode(\n this.buffer.subarray(0, this.callSession.softphone.codec.packetSize),\n );\n const rtpPacket = new RtpPacket(\n new RtpHeader({\n version: 2,\n padding: false,\n paddingSize: 0,\n extension: false,\n marker: false,\n payloadOffset: 12,\n payloadType: this.callSession.softphone.codec.id,\n sequenceNumber: this.callSession.sequenceNumber,\n timestamp: this.callSession.timestamp,\n ssrc: this.callSession.ssrc,\n csrcLength: 0,\n csrc: [],\n extensionProfile: 48862,\n extensionLength: undefined,\n extensions: [],\n }),\n temp,\n );\n this.callSession.send(\n this.callSession.srtpSession.encrypt(\n rtpPacket.payload,\n rtpPacket.header,\n ),\n );\n this.callSession.sequenceNumber += 1;\n if (this.callSession.sequenceNumber > 65535) {\n this.callSession.sequenceNumber = 0;\n }\n this.callSession.timestamp +=\n this.callSession.softphone.codec.timestampInterval;\n this.buffer = this.buffer.subarray(\n this.callSession.softphone.codec.packetSize,\n );\n if (this.finished) {\n this.emit(\"finished\");\n } else {\n setTimeout(() => this.sendPacket(), 20);\n }\n }\n }\n}\n\nexport default Streamer;\n"],"mappings":";;;;AAOA,IAAM,WAAN,cAAuB,aAAa;CAClC,SAAgB;CAChB;CACA;CACA;CAEA,YAAmB,aAA0B,QAAgB;EAC3D,OAAO;EACP,KAAK,cAAc;EACnB,KAAK,SAAS;EACd,KAAK,iBAAiB;;CAGxB,QAAe;EACb,KAAK,SAAS,KAAK;EACnB,KAAK,SAAS;EACd,KAAK,YAAY;;CAGnB,OAAc;EACZ,KAAK,SAAS,OAAO,MAAM,EAAE;;CAG/B,QAAe;EACb,KAAK,SAAS;;CAGhB,SAAgB;EACd,KAAK,SAAS;EACd,KAAK,YAAY;;CAGnB,IAAW,WAAW;EACpB,OACE,KAAK,YAAY,YACjB,KAAK,OAAO,SAAS,KAAK,YAAY,UAAU,MAAM;;CAI1D,aAAqB;EACnB,IAAI,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU;GAClC,MAAM,OAAO,KAAK,YAAY,QAAQ,OACpC,KAAK,OAAO,SAAS,GAAG,KAAK,YAAY,UAAU,MAAM,WAAW,CACrE;GACD,MAAM,YAAY,IAAI,UACpB,IAAI,UAAU;IACZ,SAAS;IACT,SAAS;IACT,aAAa;IACb,WAAW;IACX,QAAQ;IACR,eAAe;IACf,aAAa,KAAK,YAAY,UAAU,MAAM;IAC9C,gBAAgB,KAAK,YAAY;IACjC,WAAW,KAAK,YAAY;IAC5B,MAAM,KAAK,YAAY;IACvB,YAAY;IACZ,MAAM,EAAE;IACR,kBAAkB;IAClB,iBAAiB,KAAA;IACjB,YAAY,EAAE;IACf,CAAC,EACF,KACD;GACD,KAAK,YAAY,KACf,KAAK,YAAY,YAAY,QAC3B,UAAU,SACV,UAAU,OACX,CACF;GACD,KAAK,YAAY,kBAAkB;GACnC,IAAI,KAAK,YAAY,iBAAiB,OACpC,KAAK,YAAY,iBAAiB;GAEpC,KAAK,YAAY,aACf,KAAK,YAAY,UAAU,MAAM;GACnC,KAAK,SAAS,KAAK,OAAO,SACxB,KAAK,YAAY,UAAU,MAAM,WAClC;GACD,IAAI,KAAK,UACP,KAAK,KAAK,WAAW;QAErB,iBAAiB,KAAK,YAAY,EAAE,GAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"codec.mjs","names":[],"sources":["../src/codec.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { Decoder, Encoder } from \"@evan/opus\";\n\nclass Codec {\n id: number;\n name: \"OPUS/16000\" | \"OPUS/48000/2\" | \"PCMU/8000\";\n packetSize: number;\n timestampInterval: number;\n createEncoder: () => { encode: (pcm: Buffer) => Buffer };\n createDecoder: () => { decode: (audio: Buffer) => Buffer };\n constructor(name: \"OPUS/16000\" | \"OPUS/48000/2\" | \"PCMU/8000\") {\n this.name = name;\n switch (name) {\n case \"OPUS/16000\": {\n this.createEncoder = () => {\n const encoder = new Encoder({ channels: 1, sample_rate: 16000 });\n return { encode: (pcm: Buffer) => Buffer.from(encoder.encode(pcm)) };\n };\n this.createDecoder = () => {\n const decoder = new Decoder({ channels: 1, sample_rate: 16000 });\n return {\n decode: (opus: Buffer) => Buffer.from(decoder.decode(opus)),\n };\n };\n this.id = 109;\n this.packetSize = 640;\n this.timestampInterval = 320;\n break;\n }\n case \"OPUS/48000/2\": {\n this.createEncoder = () => {\n const encoder = new Encoder({ channels: 2, sample_rate: 48000 });\n return { encode: (pcm: Buffer) => Buffer.from(encoder.encode(pcm)) };\n };\n this.createDecoder = () => {\n const decoder = new Decoder({ channels: 2, sample_rate: 48000 });\n return {\n decode: (opus: Buffer) => Buffer.from(decoder.decode(opus)),\n };\n };\n this.id = 111;\n this.packetSize = 3840;\n this.timestampInterval = 960;\n break;\n }\n case \"PCMU/8000\": {\n this.createEncoder = () => {\n return { encode: (pcm: Buffer) => pcm };\n };\n this.createDecoder = () => {\n return { decode: (audio: Buffer) => audio };\n };\n this.id = 0;\n this.packetSize = 160;\n this.timestampInterval = 160;\n break;\n }\n default: {\n throw new Error(`unsupported codec: ${name}`);\n }\n }\n }\n}\n\nexport default Codec;\n"],"mappings":";;;AAGA,IAAM,QAAN,MAAY;CACV;CACA;CACA;CACA;CACA;CACA;CACA,YAAY,MAAmD;AAC7D,OAAK,OAAO;AACZ,UAAQ,MAAR;GACE,KAAK;AACH,SAAK,sBAAsB;KACzB,MAAM,UAAU,IAAI,QAAQ;MAAE,UAAU;MAAG,aAAa;MAAO,CAAC;AAChE,YAAO,EAAE,SAAS,QAAgB,OAAO,KAAK,QAAQ,OAAO,IAAI,CAAC,EAAE;;AAEtE,SAAK,sBAAsB;KACzB,MAAM,UAAU,IAAI,QAAQ;MAAE,UAAU;MAAG,aAAa;MAAO,CAAC;AAChE,YAAO,EACL,SAAS,SAAiB,OAAO,KAAK,QAAQ,OAAO,KAAK,CAAC,EAC5D;;AAEH,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,oBAAoB;AACzB;GAEF,KAAK;AACH,SAAK,sBAAsB;KACzB,MAAM,UAAU,IAAI,QAAQ;MAAE,UAAU;MAAG,aAAa;MAAO,CAAC;AAChE,YAAO,EAAE,SAAS,QAAgB,OAAO,KAAK,QAAQ,OAAO,IAAI,CAAC,EAAE;;AAEtE,SAAK,sBAAsB;KACzB,MAAM,UAAU,IAAI,QAAQ;MAAE,UAAU;MAAG,aAAa;MAAO,CAAC;AAChE,YAAO,EACL,SAAS,SAAiB,OAAO,KAAK,QAAQ,OAAO,KAAK,CAAC,EAC5D;;AAEH,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,oBAAoB;AACzB;GAEF,KAAK;AACH,SAAK,sBAAsB;AACzB,YAAO,EAAE,SAAS,QAAgB,KAAK;;AAEzC,SAAK,sBAAsB;AACzB,YAAO,EAAE,SAAS,UAAkB,OAAO;;AAE7C,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,oBAAoB;AACzB;GAEF,QACE,OAAM,IAAI,MAAM,sBAAsB,OAAO"}
1
+ {"version":3,"file":"codec.mjs","names":[],"sources":["../src/codec.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { Decoder, Encoder } from \"@evan/opus\";\n\nclass Codec {\n id: number;\n name: \"OPUS/16000\" | \"OPUS/48000/2\" | \"PCMU/8000\";\n packetSize: number;\n timestampInterval: number;\n createEncoder: () => { encode: (pcm: Buffer) => Buffer };\n createDecoder: () => { decode: (audio: Buffer) => Buffer };\n constructor(name: \"OPUS/16000\" | \"OPUS/48000/2\" | \"PCMU/8000\") {\n this.name = name;\n switch (name) {\n case \"OPUS/16000\": {\n this.createEncoder = () => {\n const encoder = new Encoder({ channels: 1, sample_rate: 16000 });\n return { encode: (pcm: Buffer) => Buffer.from(encoder.encode(pcm)) };\n };\n this.createDecoder = () => {\n const decoder = new Decoder({ channels: 1, sample_rate: 16000 });\n return {\n decode: (opus: Buffer) => Buffer.from(decoder.decode(opus)),\n };\n };\n this.id = 109;\n this.packetSize = 640;\n this.timestampInterval = 320;\n break;\n }\n case \"OPUS/48000/2\": {\n this.createEncoder = () => {\n const encoder = new Encoder({ channels: 2, sample_rate: 48000 });\n return { encode: (pcm: Buffer) => Buffer.from(encoder.encode(pcm)) };\n };\n this.createDecoder = () => {\n const decoder = new Decoder({ channels: 2, sample_rate: 48000 });\n return {\n decode: (opus: Buffer) => Buffer.from(decoder.decode(opus)),\n };\n };\n this.id = 111;\n this.packetSize = 3840;\n this.timestampInterval = 960;\n break;\n }\n case \"PCMU/8000\": {\n this.createEncoder = () => {\n return { encode: (pcm: Buffer) => pcm };\n };\n this.createDecoder = () => {\n return { decode: (audio: Buffer) => audio };\n };\n this.id = 0;\n this.packetSize = 160;\n this.timestampInterval = 160;\n break;\n }\n default: {\n throw new Error(`unsupported codec: ${name}`);\n }\n }\n }\n}\n\nexport default Codec;\n"],"mappings":";;;AAGA,IAAM,QAAN,MAAY;CACV;CACA;CACA;CACA;CACA;CACA;CACA,YAAY,MAAmD;EAC7D,KAAK,OAAO;EACZ,QAAQ,MAAR;GACE,KAAK;IACH,KAAK,sBAAsB;KACzB,MAAM,UAAU,IAAI,QAAQ;MAAE,UAAU;MAAG,aAAa;MAAO,CAAC;KAChE,OAAO,EAAE,SAAS,QAAgB,OAAO,KAAK,QAAQ,OAAO,IAAI,CAAC,EAAE;;IAEtE,KAAK,sBAAsB;KACzB,MAAM,UAAU,IAAI,QAAQ;MAAE,UAAU;MAAG,aAAa;MAAO,CAAC;KAChE,OAAO,EACL,SAAS,SAAiB,OAAO,KAAK,QAAQ,OAAO,KAAK,CAAC,EAC5D;;IAEH,KAAK,KAAK;IACV,KAAK,aAAa;IAClB,KAAK,oBAAoB;IACzB;GAEF,KAAK;IACH,KAAK,sBAAsB;KACzB,MAAM,UAAU,IAAI,QAAQ;MAAE,UAAU;MAAG,aAAa;MAAO,CAAC;KAChE,OAAO,EAAE,SAAS,QAAgB,OAAO,KAAK,QAAQ,OAAO,IAAI,CAAC,EAAE;;IAEtE,KAAK,sBAAsB;KACzB,MAAM,UAAU,IAAI,QAAQ;MAAE,UAAU;MAAG,aAAa;MAAO,CAAC;KAChE,OAAO,EACL,SAAS,SAAiB,OAAO,KAAK,QAAQ,OAAO,KAAK,CAAC,EAC5D;;IAEH,KAAK,KAAK;IACV,KAAK,aAAa;IAClB,KAAK,oBAAoB;IACzB;GAEF,KAAK;IACH,KAAK,sBAAsB;KACzB,OAAO,EAAE,SAAS,QAAgB,KAAK;;IAEzC,KAAK,sBAAsB;KACzB,OAAO,EAAE,SAAS,UAAkB,OAAO;;IAE7C,KAAK,KAAK;IACV,KAAK,aAAa;IAClB,KAAK,oBAAoB;IACzB;GAEF,SACE,MAAM,IAAI,MAAM,sBAAsB,OAAO"}
package/dist/dtmf.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dtmf.mjs","names":[],"sources":["../src/dtmf.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\n\nconst phoneChars = [\n \"0\",\n \"1\",\n \"2\",\n \"3\",\n \"4\",\n \"5\",\n \"6\",\n \"7\",\n \"8\",\n \"9\",\n \"*\",\n \"#\",\n] as const;\nconst payloads = [\n 0x00060000, 0x000600a0, 0x00060140, 0x00860320, 0x00860320, 0x00860320,\n];\n\nconst DTMF = {\n phoneChars,\n charToPayloads(char: string) {\n const index = phoneChars.indexOf(char[0] as (typeof phoneChars)[number]);\n if (index === -1) {\n throw new Error(\"invalid phone char\");\n }\n return payloads.map((payload) => {\n const temp = payload + index * 0x01000000;\n const buffer = Buffer.alloc(4);\n buffer.writeIntBE(temp, 0, 4);\n return buffer;\n });\n },\n payloadToChar(payload: Buffer) {\n const intBE = payload.readIntBE(0, 4);\n const index = (intBE - 0x00060000) / 0x01000000;\n return phoneChars[index];\n },\n};\n\nexport default DTMF;\n"],"mappings":";;AAEA,MAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AACD,MAAM,WAAW;CACf;CAAY;CAAY;CAAY;CAAY;CAAY;CAC7D;AAED,MAAM,OAAO;CACX;CACA,eAAe,MAAc;EAC3B,MAAM,QAAQ,WAAW,QAAQ,KAAK,GAAkC;AACxE,MAAI,UAAU,GACZ,OAAM,IAAI,MAAM,qBAAqB;AAEvC,SAAO,SAAS,KAAK,YAAY;GAC/B,MAAM,OAAO,UAAU,QAAQ;GAC/B,MAAM,SAAS,OAAO,MAAM,EAAE;AAC9B,UAAO,WAAW,MAAM,GAAG,EAAE;AAC7B,UAAO;IACP;;CAEJ,cAAc,SAAiB;AAG7B,SAAO,YAFO,QAAQ,UAAU,GAAG,EACf,GAAG,UAAc;;CAGxC"}
1
+ {"version":3,"file":"dtmf.mjs","names":[],"sources":["../src/dtmf.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\n\nconst phoneChars = [\n \"0\",\n \"1\",\n \"2\",\n \"3\",\n \"4\",\n \"5\",\n \"6\",\n \"7\",\n \"8\",\n \"9\",\n \"*\",\n \"#\",\n] as const;\nconst payloads = [\n 0x00060000, 0x000600a0, 0x00060140, 0x00860320, 0x00860320, 0x00860320,\n];\n\nconst DTMF = {\n phoneChars,\n charToPayloads(char: string) {\n const index = phoneChars.indexOf(char[0] as (typeof phoneChars)[number]);\n if (index === -1) {\n throw new Error(\"invalid phone char\");\n }\n return payloads.map((payload) => {\n const temp = payload + index * 0x01000000;\n const buffer = Buffer.alloc(4);\n buffer.writeIntBE(temp, 0, 4);\n return buffer;\n });\n },\n payloadToChar(payload: Buffer) {\n const intBE = payload.readIntBE(0, 4);\n const index = (intBE - 0x00060000) / 0x01000000;\n return phoneChars[index];\n },\n};\n\nexport default DTMF;\n"],"mappings":";;AAEA,MAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AACD,MAAM,WAAW;CACf;CAAY;CAAY;CAAY;CAAY;CAAY;CAC7D;AAED,MAAM,OAAO;CACX;CACA,eAAe,MAAc;EAC3B,MAAM,QAAQ,WAAW,QAAQ,KAAK,GAAkC;EACxE,IAAI,UAAU,IACZ,MAAM,IAAI,MAAM,qBAAqB;EAEvC,OAAO,SAAS,KAAK,YAAY;GAC/B,MAAM,OAAO,UAAU,QAAQ;GAC/B,MAAM,SAAS,OAAO,MAAM,EAAE;GAC9B,OAAO,WAAW,MAAM,GAAG,EAAE;GAC7B,OAAO;IACP;;CAEJ,cAAc,SAAiB;EAG7B,OAAO,YAFO,QAAQ,UAAU,GAAG,EACf,GAAG,UAAc;;CAGxC"}
@@ -47,6 +47,10 @@ declare abstract class CallSession extends EventEmitter {
47
47
  sequenceNumber: number;
48
48
  timestamp: number;
49
49
  constructor(softphone: Softphone, sipMessage: InboundMessage);
50
+ static createBoundSocket(): Promise<{
51
+ socket: dgram.Socket;
52
+ port: number;
53
+ }>;
50
54
  set remoteKey(key: string);
51
55
  get callId(): string | undefined;
52
56
  send(data: string | Buffer): void;
@@ -65,7 +69,7 @@ declare abstract class CallSession extends EventEmitter {
65
69
  //#endregion
66
70
  //#region src/call-session/outbound.d.ts
67
71
  declare class OutboundCallSession extends CallSession {
68
- constructor(softphone: Softphone, answerMessage: InboundMessage);
72
+ constructor(softphone: Softphone, answerMessage: InboundMessage, socket: dgram.Socket);
69
73
  init(): void;
70
74
  cancel(): Promise<void>;
71
75
  get sessionId(): string;
@@ -85,7 +89,10 @@ declare class Softphone extends EventEmitter {
85
89
  private instanceId;
86
90
  private registerCallId;
87
91
  register(): Promise<void>;
88
- enableDebugMode(): void;
92
+ enableDebugMode(options?: {
93
+ inboundPrefix: string;
94
+ outboundPrefix: string;
95
+ }): void;
89
96
  revoke(): void;
90
97
  send(message: OutboundMessage, waitForReply?: true): Promise<InboundMessage>;
91
98
  send(message: OutboundMessage, waitForReply?: false): Promise<undefined>;
@@ -101,4 +108,4 @@ declare class InboundCallSession extends CallSession {
101
108
  }
102
109
  //#endregion
103
110
  export { Streamer as a, CallSession as i, Softphone as n, OutboundCallSession as r, InboundCallSession as t };
104
- //# sourceMappingURL=inbound--wGoGqLS.d.mts.map
111
+ //# sourceMappingURL=inbound-BhY2IDed.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound-BhY2IDed.d.mts","names":[],"sources":["../src/call-session/streamer.ts","../src/call-session/index.ts","../src/call-session/outbound.ts","../src/index.ts","../src/call-session/inbound.ts"],"mappings":";;;;;;;;;;;;cAOM,QAAA,SAAiB,YAAA;EACd,MAAA;EAAA,QACC,WAAA;EAAA,QACA,MAAA;EAAA,QACA,cAAA;cAEW,WAAA,EAAa,WAAA,EAAa,MAAA,EAAQ,MAAA;EAO9C,KAAA,CAAA;EAMA,IAAA,CAAA;EAIA,KAAA,CAAA;EAIA,MAAA,CAAA;EAAA,IAKI,QAAA,CAAA;EAAA,QAOH,UAAA;AAAA;;;KC/BL,QAAA,WAAmB,IAAA,CAAK,UAAA;AAAA,uBAKd,WAAA,SAAoB,YAAA;EAC1B,SAAA,EAAW,SAAA;EACX,UAAA,EAAY,cAAA;EACZ,MAAA,EAAS,KAAA,CAAM,MAAA;EACf,SAAA;EACA,UAAA;EACA,QAAA;EACA,UAAA;EACA,QAAA;EACA,WAAA,EAAc,WAAA;EACd,OAAA;IAAW,MAAA,GAAS,GAAA,EAAK,MAAA,KAAW,MAAA;EAAA;EACpC,OAAA;IAAW,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,MAAA;EAAA;EACtC,GAAA;EAGA,IAAA;EACA,cAAA;EACA,SAAA;cAEY,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,cAAA;EAAA,OAgBjC,iBAAA,CAAA,GAAiB,OAAA;YAEA,KAAA,CAAM,MAAA;;;MAmBhC,SAAA,CAAU,GAAA;EAAA,IAcV,MAAA,CAAA;EAIJ,IAAA,CAAK,IAAA,WAAe,MAAA;EAId,MAAA,CAAA,GAAM,OAAA;EAaZ,QAAA,CAAS,IAAA,EAAM,QAAA;EA8BT,SAAA,CAAU,CAAA,UAAW,KAAA,YAAW,OAAA;EAYtC,WAAA,CAAY,KAAA,EAAO,MAAA,GAAM,QAAA;EAOzB,UAAA,CAAW,SAAA,EAAW,SAAA;EAAA,UAOnB,kBAAA,CAAA;EAAA,UAuDA,OAAA,CAAA;EAQG,QAAA,CAAS,UAAA,WAAkB,OAAA;EAqC3B,aAAA,CAAc,SAAA,YAAkB,OAAA;EA+BhC,IAAA,CAAA,GAAI,OAAA;EAIJ,MAAA,CAAA,GAAM,OAAA;AAAA;;;cCxSf,mBAAA,SAA4B,WAAA;cAE9B,SAAA,EAAW,SAAA,EACX,aAAA,EAAe,cAAA,EACf,MAAA,EAAQ,KAAA,CAAM,MAAA;EAYT,IAAA,CAAA;EAiCM,MAAA,CAAA,GAAM,OAAA;EAAA,IAcR,SAAA,CAAA;EAAA,IAKA,OAAA,CAAA;AAAA;;;cCxDP,SAAA,SAAkB,YAAA;EACf,OAAA,EAAS,gBAAA;EACT,MAAA,EAAQ,SAAA;EACR,KAAA,EAAO,KAAA;EAEP,UAAA;EACA,SAAA;EAAA,QAEC,cAAA;EAAA,QACA,SAAA;cAEW,OAAA,EAAS,gBAAA;EAAA,QAoDpB,UAAA;EAAA,QACA,cAAA;EAEK,QAAA,CAAA,GAAQ,OAAA;EA0Ed,eAAA,CACL,OAAA;;;;EAeK,MAAA,CAAA;EAOA,IAAA,CACL,OAAA,EAAS,eAAA,EACT,YAAA,UACC,OAAA,CAAQ,cAAA;EACJ,IAAA,CACL,OAAA,EAAS,eAAA,EACT,YAAA,WACC,OAAA;EAyBU,MAAA,CAAO,aAAA,EAAe,cAAA,GAAc,OAAA,CAAA,kBAAA;EAOpC,OAAA,CAAQ,aAAA,EAAe,cAAA,GAAc,OAAA;EAKrC,IAAA,CAAK,MAAA,WAAc,OAAA,CAAA,mBAAA;AAAA;;;cC5N5B,kBAAA,SAA2B,WAAA;cACZ,SAAA,EAAW,SAAA,EAAW,aAAA,EAAe,cAAA;EAY3C,MAAA,CAAA,GAAM,OAAA;AAAA"}
@@ -5,8 +5,8 @@ import { t as Codec } from "./codec-C-VrtVkq.cjs";
5
5
  import { t as SoftPhoneOptions } from "./types-DZxCsbZE.cjs";
6
6
  import EventEmitter from "node:events";
7
7
  import { TLSSocket } from "node:tls";
8
- import { Buffer } from "node:buffer";
9
8
  import dgram from "node:dgram";
9
+ import { Buffer } from "node:buffer";
10
10
  import { RtpPacket, SrtpSession } from "werift-rtp";
11
11
 
12
12
  //#region src/call-session/streamer.d.ts
@@ -47,6 +47,10 @@ declare abstract class CallSession extends EventEmitter {
47
47
  sequenceNumber: number;
48
48
  timestamp: number;
49
49
  constructor(softphone: Softphone, sipMessage: InboundMessage);
50
+ static createBoundSocket(): Promise<{
51
+ socket: dgram.Socket;
52
+ port: number;
53
+ }>;
50
54
  set remoteKey(key: string);
51
55
  get callId(): string | undefined;
52
56
  send(data: string | Buffer): void;
@@ -65,7 +69,7 @@ declare abstract class CallSession extends EventEmitter {
65
69
  //#endregion
66
70
  //#region src/call-session/outbound.d.ts
67
71
  declare class OutboundCallSession extends CallSession {
68
- constructor(softphone: Softphone, answerMessage: InboundMessage);
72
+ constructor(softphone: Softphone, answerMessage: InboundMessage, socket: dgram.Socket);
69
73
  init(): void;
70
74
  cancel(): Promise<void>;
71
75
  get sessionId(): string;
@@ -85,7 +89,10 @@ declare class Softphone extends EventEmitter {
85
89
  private instanceId;
86
90
  private registerCallId;
87
91
  register(): Promise<void>;
88
- enableDebugMode(): void;
92
+ enableDebugMode(options?: {
93
+ inboundPrefix: string;
94
+ outboundPrefix: string;
95
+ }): void;
89
96
  revoke(): void;
90
97
  send(message: OutboundMessage, waitForReply?: true): Promise<InboundMessage>;
91
98
  send(message: OutboundMessage, waitForReply?: false): Promise<undefined>;
@@ -101,4 +108,4 @@ declare class InboundCallSession extends CallSession {
101
108
  }
102
109
  //#endregion
103
110
  export { Streamer as a, CallSession as i, Softphone as n, OutboundCallSession as r, InboundCallSession as t };
104
- //# sourceMappingURL=inbound-DquvTSj1.d.cts.map
111
+ //# sourceMappingURL=inbound-NTxLsDeV.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound-NTxLsDeV.d.cts","names":[],"sources":["../src/call-session/streamer.ts","../src/call-session/index.ts","../src/call-session/outbound.ts","../src/index.ts","../src/call-session/inbound.ts"],"mappings":";;;;;;;;;;;;cAOM,QAAA,SAAiB,YAAA;EACd,MAAA;EAAA,QACC,WAAA;EAAA,QACA,MAAA;EAAA,QACA,cAAA;cAEW,WAAA,EAAa,WAAA,EAAa,MAAA,EAAQ,MAAA;EAO9C,KAAA,CAAA;EAMA,IAAA,CAAA;EAIA,KAAA,CAAA;EAIA,MAAA,CAAA;EAAA,IAKI,QAAA,CAAA;EAAA,QAOH,UAAA;AAAA;;;KC/BL,QAAA,WAAmB,IAAA,CAAK,UAAA;AAAA,uBAKd,WAAA,SAAoB,YAAA;EAC1B,SAAA,EAAW,SAAA;EACX,UAAA,EAAY,cAAA;EACZ,MAAA,EAAS,KAAA,CAAM,MAAA;EACf,SAAA;EACA,UAAA;EACA,QAAA;EACA,UAAA;EACA,QAAA;EACA,WAAA,EAAc,WAAA;EACd,OAAA;IAAW,MAAA,GAAS,GAAA,EAAK,MAAA,KAAW,MAAA;EAAA;EACpC,OAAA;IAAW,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,MAAA;EAAA;EACtC,GAAA;EAGA,IAAA;EACA,cAAA;EACA,SAAA;cAEY,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,cAAA;EAAA,OAgBjC,iBAAA,CAAA,GAAiB,OAAA;YAEA,KAAA,CAAM,MAAA;;;MAmBhC,SAAA,CAAU,GAAA;EAAA,IAcV,MAAA,CAAA;EAIJ,IAAA,CAAK,IAAA,WAAe,MAAA;EAId,MAAA,CAAA,GAAM,OAAA;EAaZ,QAAA,CAAS,IAAA,EAAM,QAAA;EA8BT,SAAA,CAAU,CAAA,UAAW,KAAA,YAAW,OAAA;EAYtC,WAAA,CAAY,KAAA,EAAO,MAAA,GAAM,QAAA;EAOzB,UAAA,CAAW,SAAA,EAAW,SAAA;EAAA,UAOnB,kBAAA,CAAA;EAAA,UAuDA,OAAA,CAAA;EAQG,QAAA,CAAS,UAAA,WAAkB,OAAA;EAqC3B,aAAA,CAAc,SAAA,YAAkB,OAAA;EA+BhC,IAAA,CAAA,GAAI,OAAA;EAIJ,MAAA,CAAA,GAAM,OAAA;AAAA;;;cCxSf,mBAAA,SAA4B,WAAA;cAE9B,SAAA,EAAW,SAAA,EACX,aAAA,EAAe,cAAA,EACf,MAAA,EAAQ,KAAA,CAAM,MAAA;EAYT,IAAA,CAAA;EAiCM,MAAA,CAAA,GAAM,OAAA;EAAA,IAcR,SAAA,CAAA;EAAA,IAKA,OAAA,CAAA;AAAA;;;cCxDP,SAAA,SAAkB,YAAA;EACf,OAAA,EAAS,gBAAA;EACT,MAAA,EAAQ,SAAA;EACR,KAAA,EAAO,KAAA;EAEP,UAAA;EACA,SAAA;EAAA,QAEC,cAAA;EAAA,QACA,SAAA;cAEW,OAAA,EAAS,gBAAA;EAAA,QAoDpB,UAAA;EAAA,QACA,cAAA;EAEK,QAAA,CAAA,GAAQ,OAAA;EA0Ed,eAAA,CACL,OAAA;;;;EAeK,MAAA,CAAA;EAOA,IAAA,CACL,OAAA,EAAS,eAAA,EACT,YAAA,UACC,OAAA,CAAQ,cAAA;EACJ,IAAA,CACL,OAAA,EAAS,eAAA,EACT,YAAA,WACC,OAAA;EAyBU,MAAA,CAAO,aAAA,EAAe,cAAA,GAAc,OAAA,CAAA,kBAAA;EAOpC,OAAA,CAAQ,aAAA,EAAe,cAAA,GAAc,OAAA;EAKrC,IAAA,CAAK,MAAA,WAAc,OAAA,CAAA,mBAAA;AAAA;;;cC5N5B,kBAAA,SAA2B,WAAA;cACZ,SAAA,EAAW,SAAA,EAAW,aAAA,EAAe,cAAA;EAY3C,MAAA,CAAA,GAAM,OAAA;AAAA"}
package/dist/index.cjs CHANGED
@@ -6,6 +6,7 @@ const require_sip_message_outbound_index = require("./sip-message/outbound/index
6
6
  const require_sip_message_outbound_request = require("./sip-message/outbound/request.cjs");
7
7
  const require_sip_message_outbound_response = require("./sip-message/outbound/response.cjs");
8
8
  require("./sip-message/index.cjs");
9
+ const require_call_session_index = require("./call-session/index.cjs");
9
10
  const require_call_session_inbound = require("./call-session/inbound.cjs");
10
11
  const require_call_session_outbound = require("./call-session/outbound.cjs");
11
12
  let node_events = require("node:events");
@@ -38,6 +39,11 @@ var Softphone = class extends node_events.default {
38
39
  }, () => {
39
40
  this.connected = true;
40
41
  });
42
+ const tlsWrite = this.client.write.bind(this.client);
43
+ this.client.write = (message) => {
44
+ this.emit("outboundMessage", message.toString());
45
+ return tlsWrite(message);
46
+ };
41
47
  let cache = "";
42
48
  this.client.on("data", (data) => {
43
49
  cache += data.toString("utf-8");
@@ -73,6 +79,7 @@ var Softphone = class extends node_events.default {
73
79
  });
74
80
  const inboundMessage = await this.send(requestMessage, true);
75
81
  if (inboundMessage.subject.startsWith("SIP/2.0 200 ")) return;
82
+ if (!inboundMessage.subject.startsWith("SIP/2.0 401 ")) throw new Error(`Failed to register: ${inboundMessage.subject}`);
76
83
  const nonce = inboundMessage.getHeader("Www-Authenticate").match(/, nonce="(.+?)"/)[1];
77
84
  const newMessage = requestMessage.fork();
78
85
  newMessage.headers.Authorization = require_utils.generateAuthorization(this.sipInfo, nonce, "REGISTER");
@@ -99,13 +106,16 @@ var Softphone = class extends node_events.default {
99
106
  this.emit("invite", inboundMessage);
100
107
  });
101
108
  }
102
- enableDebugMode() {
103
- this.on("message", (message) => console.log(`Receiving...(${/* @__PURE__ */ new Date()})\n${message.toString()}`));
104
- const tlsWrite = this.client.write.bind(this.client);
105
- this.client.write = (message) => {
106
- console.log(`Sending...(${/* @__PURE__ */ new Date()})\n${message}`);
107
- return tlsWrite(message);
108
- };
109
+ enableDebugMode(options = {
110
+ inboundPrefix: "Receiving...\n",
111
+ outboundPrefix: "Sending...\n"
112
+ }) {
113
+ this.on("message", (message) => {
114
+ console.log(`${options.inboundPrefix}(${/* @__PURE__ */ new Date()})\n${message.toString()}`);
115
+ });
116
+ this.on("outboundMessage", (message) => {
117
+ console.log(`${options.outboundPrefix}(${/* @__PURE__ */ new Date()})\n${message}`);
118
+ });
109
119
  }
110
120
  revoke() {
111
121
  clearInterval(this.intervalHandle);
@@ -136,13 +146,14 @@ var Softphone = class extends node_events.default {
136
146
  await this.send(newMessage);
137
147
  }
138
148
  async call(callee) {
149
+ const { socket, port } = await require_call_session_index.createBoundSocket();
139
150
  const offerSDP = `
140
151
  v=0
141
152
  o=- ${Date.now()} 0 IN IP4 ${this.client.localAddress}
142
153
  s=rc-softphone-ts
143
154
  c=IN IP4 ${this.client.localAddress}
144
155
  t=0 0
145
- m=audio ${require_utils.randomInt()} RTP/SAVP ${this.codec.id} 101
156
+ m=audio ${port} RTP/SAVP ${this.codec.id} 101
146
157
  a=rtpmap:${this.codec.id} ${this.codec.name}
147
158
  a=rtpmap:101 telephone-event/8000
148
159
  a=fmtp:101 0-15
@@ -167,7 +178,7 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${require_utils.localKey}
167
178
  const newMessage = inviteMessage.fork();
168
179
  newMessage.headers["Proxy-Authorization"] = require_utils.generateAuthorization(this.sipInfo, nonce, "INVITE");
169
180
  const progressMessage = await this.send(newMessage, true);
170
- const outboundCallSession = new require_call_session_outbound(this, progressMessage);
181
+ const outboundCallSession = new require_call_session_outbound(this, progressMessage, socket);
171
182
  outboundCallSession.sdp = offerSDP;
172
183
  return outboundCallSession;
173
184
  }
package/dist/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as Softphone } from "./inbound-DquvTSj1.cjs";
1
+ import { n as Softphone } from "./inbound-NTxLsDeV.cjs";
2
2
  export = Softphone;
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as Softphone } from "./inbound--wGoGqLS.mjs";
1
+ import { n as Softphone } from "./inbound-BhY2IDed.mjs";
2
2
  export { Softphone as default };
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
1
  import Codec from "./codec.mjs";
2
- import { branch, generateAuthorization, localKey, randomInt, uuid } from "./utils.mjs";
2
+ import { branch, generateAuthorization, localKey, uuid } from "./utils.mjs";
3
3
  import InboundMessage from "./sip-message/inbound/index.mjs";
4
4
  import OutboundMessage from "./sip-message/outbound/index.mjs";
5
5
  import RequestMessage from "./sip-message/outbound/request.mjs";
6
6
  import ResponseMessage from "./sip-message/outbound/response.mjs";
7
7
  import "./sip-message/index.mjs";
8
+ import CallSession from "./call-session/index.mjs";
8
9
  import InboundCallSession from "./call-session/inbound.mjs";
9
10
  import OutboundCallSession from "./call-session/outbound.mjs";
10
11
  import EventEmitter from "node:events";
@@ -34,6 +35,11 @@ var Softphone = class extends EventEmitter {
34
35
  }, () => {
35
36
  this.connected = true;
36
37
  });
38
+ const tlsWrite = this.client.write.bind(this.client);
39
+ this.client.write = (message) => {
40
+ this.emit("outboundMessage", message.toString());
41
+ return tlsWrite(message);
42
+ };
37
43
  let cache = "";
38
44
  this.client.on("data", (data) => {
39
45
  cache += data.toString("utf-8");
@@ -69,6 +75,7 @@ var Softphone = class extends EventEmitter {
69
75
  });
70
76
  const inboundMessage = await this.send(requestMessage, true);
71
77
  if (inboundMessage.subject.startsWith("SIP/2.0 200 ")) return;
78
+ if (!inboundMessage.subject.startsWith("SIP/2.0 401 ")) throw new Error(`Failed to register: ${inboundMessage.subject}`);
72
79
  const nonce = inboundMessage.getHeader("Www-Authenticate").match(/, nonce="(.+?)"/)[1];
73
80
  const newMessage = requestMessage.fork();
74
81
  newMessage.headers.Authorization = generateAuthorization(this.sipInfo, nonce, "REGISTER");
@@ -95,13 +102,16 @@ var Softphone = class extends EventEmitter {
95
102
  this.emit("invite", inboundMessage);
96
103
  });
97
104
  }
98
- enableDebugMode() {
99
- this.on("message", (message) => console.log(`Receiving...(${/* @__PURE__ */ new Date()})\n${message.toString()}`));
100
- const tlsWrite = this.client.write.bind(this.client);
101
- this.client.write = (message) => {
102
- console.log(`Sending...(${/* @__PURE__ */ new Date()})\n${message}`);
103
- return tlsWrite(message);
104
- };
105
+ enableDebugMode(options = {
106
+ inboundPrefix: "Receiving...\n",
107
+ outboundPrefix: "Sending...\n"
108
+ }) {
109
+ this.on("message", (message) => {
110
+ console.log(`${options.inboundPrefix}(${/* @__PURE__ */ new Date()})\n${message.toString()}`);
111
+ });
112
+ this.on("outboundMessage", (message) => {
113
+ console.log(`${options.outboundPrefix}(${/* @__PURE__ */ new Date()})\n${message}`);
114
+ });
105
115
  }
106
116
  revoke() {
107
117
  clearInterval(this.intervalHandle);
@@ -132,13 +142,14 @@ var Softphone = class extends EventEmitter {
132
142
  await this.send(newMessage);
133
143
  }
134
144
  async call(callee) {
145
+ const { socket, port } = await CallSession.createBoundSocket();
135
146
  const offerSDP = `
136
147
  v=0
137
148
  o=- ${Date.now()} 0 IN IP4 ${this.client.localAddress}
138
149
  s=rc-softphone-ts
139
150
  c=IN IP4 ${this.client.localAddress}
140
151
  t=0 0
141
- m=audio ${randomInt()} RTP/SAVP ${this.codec.id} 101
152
+ m=audio ${port} RTP/SAVP ${this.codec.id} 101
142
153
  a=rtpmap:${this.codec.id} ${this.codec.name}
143
154
  a=rtpmap:101 telephone-event/8000
144
155
  a=fmtp:101 0-15
@@ -163,7 +174,7 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}
163
174
  const newMessage = inviteMessage.fork();
164
175
  newMessage.headers["Proxy-Authorization"] = generateAuthorization(this.sipInfo, nonce, "INVITE");
165
176
  const progressMessage = await this.send(newMessage, true);
166
- const outboundCallSession = new OutboundCallSession(this, progressMessage);
177
+ const outboundCallSession = new OutboundCallSession(this, progressMessage, socket);
167
178
  outboundCallSession.sdp = offerSDP;
168
179
  return outboundCallSession;
169
180
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import EventEmitter from \"node:events\";\nimport tls, { type TLSSocket } from \"node:tls\";\n\nimport waitFor from \"wait-for-async\";\n\nimport InboundCallSession from \"./call-session/inbound.js\";\nimport OutboundCallSession from \"./call-session/outbound.js\";\nimport Codec from \"./codec.js\";\nimport {\n InboundMessage,\n OutboundMessage,\n RequestMessage,\n ResponseMessage,\n} from \"./sip-message/index.js\";\nimport type { SoftPhoneOptions } from \"./types.js\";\nimport {\n branch,\n generateAuthorization,\n localKey,\n randomInt,\n uuid,\n} from \"./utils.js\";\n\nclass Softphone extends EventEmitter {\n public sipInfo: SoftPhoneOptions;\n public client: TLSSocket;\n public codec: Codec;\n\n public fakeDomain = `${uuid()}.invalid`;\n public fakeEmail = `${uuid()}@${this.fakeDomain}`;\n\n private intervalHandle?: NodeJS.Timeout;\n private connected = false;\n\n public constructor(sipInfo: SoftPhoneOptions) {\n super();\n if (sipInfo.codec === undefined) {\n sipInfo.codec = \"OPUS/16000\";\n }\n this.codec = new Codec(sipInfo.codec);\n this.sipInfo = sipInfo;\n if (this.sipInfo.domain === undefined) {\n this.sipInfo.domain = \"sip.ringcentral.com\";\n }\n if (this.sipInfo.outboundProxy === undefined) {\n this.sipInfo.outboundProxy = \"sip10.ringcentral.com:5096\";\n }\n const tokens = this.sipInfo.outboundProxy!.split(\":\");\n this.client = tls.connect(\n {\n host: tokens[0],\n port: parseInt(tokens[1], 10),\n rejectUnauthorized: !this.sipInfo.ignoreTlsCertErrors,\n },\n () => {\n this.connected = true;\n },\n );\n\n let cache = \"\";\n this.client.on(\"data\", (data) => {\n cache += data.toString(\"utf-8\");\n if (!cache.endsWith(\"\\r\\n\")) {\n return; // haven't received a complete message yet\n }\n // received two empty body messages\n const tempMessages = cache\n .split(\"\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n .filter((message) => message.trim() !== \"\");\n cache = \"\";\n for (let i = 0; i < tempMessages.length; i++) {\n if (!tempMessages[i].includes(\"Content-Length: \")) {\n tempMessages[i] = `${tempMessages[i]}\\r\\nContent-Length: 0`;\n }\n }\n for (const message of tempMessages) {\n this.emit(\"message\", InboundMessage.fromString(message));\n }\n });\n }\n\n private instanceId = uuid();\n private registerCallId = uuid();\n\n public async register() {\n if (!this.connected) {\n await waitFor({\n interval: 100,\n times: 100,\n condition: () => this.connected,\n });\n if (!this.connected) {\n throw new Error(\"Failed to register: connect to TLS timeout\");\n }\n }\n const sipRegister = async () => {\n const fromTag = uuid();\n const requestMessage = new RequestMessage(\n `REGISTER sip:${this.sipInfo.domain} SIP/2.0`,\n {\n Via: `SIP/2.0/TLS ${this.client.localAddress}:${this.client.localPort};rport;branch=${branch()};alias`,\n \"Max-Forwards\": \"70\",\n From: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>;tag=${fromTag}`,\n To: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>`,\n \"Call-ID\": this.registerCallId,\n Contact: `<sip:${this.sipInfo.username}@${this.client.localAddress}:${this.client.localPort};transport=TLS;ob>;reg-id=1;+sip.instance=\"<urn:uuid:${this.instanceId}>\"`,\n Expires: 3600,\n Allow:\n \"PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS\",\n },\n );\n const inboundMessage = await this.send(requestMessage, true);\n if (inboundMessage.subject.startsWith(\"SIP/2.0 200 \")) {\n // sometimes the server will return 200 OK directly\n return;\n }\n const wwwAuth = inboundMessage.getHeader(\"Www-Authenticate\")!;\n const nonce = wwwAuth.match(/, nonce=\"(.+?)\"/)![1];\n const newMessage = requestMessage.fork();\n newMessage.headers.Authorization = generateAuthorization(\n this.sipInfo,\n nonce,\n \"REGISTER\",\n );\n const message = await this.send(newMessage, true);\n if (!message.subject.startsWith(\"SIP/2.0 200 \")) {\n throw new Error(`Failed to register: ${message.subject}`);\n }\n };\n await sipRegister();\n this.intervalHandle = setInterval(\n () => {\n sipRegister().catch((error) => {\n this.emit(\"registrationError\", error);\n });\n },\n 30 * 1000, // refresh registration every 30 seconds\n );\n this.on(\"message\", (inboundMessage) => {\n if (!inboundMessage.subject.startsWith(\"INVITE sip:\")) {\n return;\n }\n const outboundMessage = new OutboundMessage(\"SIP/2.0 100 Trying\", {\n Via: inboundMessage.headers.Via,\n \"Call-ID\": inboundMessage.getHeader(\"Call-ID\"),\n From: inboundMessage.headers.From,\n To: inboundMessage.headers.To,\n CSeq: inboundMessage.headers.CSeq,\n \"Content-Length\": \"0\",\n });\n this.send(outboundMessage);\n this.emit(\"invite\", inboundMessage);\n });\n }\n\n public enableDebugMode() {\n this.on(\"message\", (message) =>\n console.log(`Receiving...(${new Date()})\\n${message.toString()}`),\n );\n const tlsWrite = this.client.write.bind(this.client);\n this.client.write = (message) => {\n console.log(`Sending...(${new Date()})\\n${message}`);\n return tlsWrite(message);\n };\n }\n\n public revoke() {\n clearInterval(this.intervalHandle);\n this.removeAllListeners();\n this.client.removeAllListeners();\n this.client.destroy();\n }\n\n public send(\n message: OutboundMessage,\n waitForReply?: true,\n ): Promise<InboundMessage>;\n public send(\n message: OutboundMessage,\n waitForReply?: false,\n ): Promise<undefined>;\n public send(message: OutboundMessage, waitForReply = false) {\n this.client.write(message.toString());\n if (!waitForReply) {\n return Promise.resolve(undefined);\n }\n return new Promise<InboundMessage>((resolve) => {\n const messageListerner = (inboundMessage: InboundMessage) => {\n // \"12563 INVITE\" vs \"12563 ACK\"\n if (\n inboundMessage.headers.CSeq.trim().split(/\\s+/)[0] !==\n message.headers.CSeq.trim().split(/\\s+/)[0]\n ) {\n return;\n }\n if (inboundMessage.subject.startsWith(\"SIP/2.0 100 \")) {\n return; // ignore\n }\n this.off(\"message\", messageListerner);\n resolve(inboundMessage);\n };\n this.on(\"message\", messageListerner);\n });\n }\n\n public async answer(inviteMessage: InboundMessage) {\n const inboundCallSession = new InboundCallSession(this, inviteMessage);\n await inboundCallSession.answer();\n return inboundCallSession;\n }\n\n // decline an inbound call\n public async decline(inviteMessage: InboundMessage) {\n const newMessage = new ResponseMessage(inviteMessage, 603);\n await this.send(newMessage);\n }\n\n public async call(callee: string) {\n const offerSDP = `\nv=0\no=- ${Date.now()} 0 IN IP4 ${this.client.localAddress}\ns=rc-softphone-ts\nc=IN IP4 ${this.client.localAddress}\nt=0 0\nm=audio ${randomInt()} RTP/SAVP ${this.codec.id} 101\na=rtpmap:${this.codec.id} ${this.codec.name}\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-15\na=sendrecv\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}\n `.trim();\n const inviteMessage = new RequestMessage(\n `INVITE sip:${callee}@${this.sipInfo.domain} SIP/2.0`,\n {\n Via: `SIP/2.0/TLS ${this.client.localAddress}:${this.client.localPort};rport;branch=${branch()};alias`,\n \"Max-Forwards\": 70,\n From: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>;tag=${uuid()}`,\n To: `<sip:${callee}@${this.sipInfo.domain}>`,\n Contact: ` <sip:${this.sipInfo.username}@${this.client.localAddress}:${this.client.localPort};transport=TLS;ob>`,\n \"Call-ID\": uuid(),\n Route: `<sip:${this.sipInfo.outboundProxy};transport=tls;lr>`,\n Allow: `PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS`,\n Supported: `replaces, 100rel, timer, norefersub`,\n \"Session-Expires\": 1800,\n \"Min-SE\": 90,\n \"Content-Type\": \"application/sdp\",\n },\n offerSDP,\n );\n const inboundMessage = await this.send(inviteMessage, true);\n const proxyAuthenticate = inboundMessage.getHeader(\"Proxy-Authenticate\")!;\n const nonce = proxyAuthenticate.match(/, nonce=\"(.+?)\"/)![1];\n const newMessage = inviteMessage.fork();\n newMessage.headers[\"Proxy-Authorization\"] = generateAuthorization(\n this.sipInfo,\n nonce,\n \"INVITE\",\n );\n const progressMessage = await this.send(newMessage, true);\n const outboundCallSession = new OutboundCallSession(this, progressMessage);\n outboundCallSession.sdp = offerSDP;\n return outboundCallSession;\n }\n}\n\nexport default Softphone;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import EventEmitter from \"node:events\";\nimport tls, { type TLSSocket } from \"node:tls\";\n\nimport waitFor from \"wait-for-async\";\n\nimport InboundCallSession from \"./call-session/inbound.js\";\nimport CallSession from \"./call-session/index.js\";\nimport OutboundCallSession from \"./call-session/outbound.js\";\nimport Codec from \"./codec.js\";\nimport {\n InboundMessage,\n OutboundMessage,\n RequestMessage,\n ResponseMessage,\n} from \"./sip-message/index.js\";\nimport type { SoftPhoneOptions } from \"./types.js\";\nimport { branch, generateAuthorization, localKey, uuid } from \"./utils.js\";\n\nclass Softphone extends EventEmitter {\n public sipInfo: SoftPhoneOptions;\n public client: TLSSocket;\n public codec: Codec;\n\n public fakeDomain = `${uuid()}.invalid`;\n public fakeEmail = `${uuid()}@${this.fakeDomain}`;\n\n private intervalHandle?: NodeJS.Timeout;\n private connected = false;\n\n public constructor(sipInfo: SoftPhoneOptions) {\n super();\n if (sipInfo.codec === undefined) {\n sipInfo.codec = \"OPUS/16000\";\n }\n this.codec = new Codec(sipInfo.codec);\n this.sipInfo = sipInfo;\n if (this.sipInfo.domain === undefined) {\n this.sipInfo.domain = \"sip.ringcentral.com\";\n }\n if (this.sipInfo.outboundProxy === undefined) {\n this.sipInfo.outboundProxy = \"sip10.ringcentral.com:5096\";\n }\n const tokens = this.sipInfo.outboundProxy!.split(\":\");\n this.client = tls.connect(\n {\n host: tokens[0],\n port: parseInt(tokens[1], 10),\n rejectUnauthorized: !this.sipInfo.ignoreTlsCertErrors,\n },\n () => {\n this.connected = true;\n },\n );\n const tlsWrite = this.client.write.bind(this.client);\n this.client.write = (message) => {\n this.emit(\"outboundMessage\", message.toString());\n return tlsWrite(message);\n };\n\n let cache = \"\";\n this.client.on(\"data\", (data) => {\n cache += data.toString(\"utf-8\");\n if (!cache.endsWith(\"\\r\\n\")) {\n return; // haven't received a complete message yet\n }\n // received two empty body messages\n const tempMessages = cache\n .split(\"\\r\\nContent-Length: 0\\r\\n\\r\\n\")\n .filter((message) => message.trim() !== \"\");\n cache = \"\";\n for (let i = 0; i < tempMessages.length; i++) {\n if (!tempMessages[i].includes(\"Content-Length: \")) {\n tempMessages[i] = `${tempMessages[i]}\\r\\nContent-Length: 0`;\n }\n }\n for (const message of tempMessages) {\n this.emit(\"message\", InboundMessage.fromString(message));\n }\n });\n }\n\n private instanceId = uuid();\n private registerCallId = uuid();\n\n public async register() {\n if (!this.connected) {\n await waitFor({\n interval: 100,\n times: 100,\n condition: () => this.connected,\n });\n if (!this.connected) {\n throw new Error(\"Failed to register: connect to TLS timeout\");\n }\n }\n const sipRegister = async () => {\n const fromTag = uuid();\n const requestMessage = new RequestMessage(\n `REGISTER sip:${this.sipInfo.domain} SIP/2.0`,\n {\n Via: `SIP/2.0/TLS ${this.client.localAddress}:${this.client.localPort};rport;branch=${branch()};alias`,\n \"Max-Forwards\": \"70\",\n From: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>;tag=${fromTag}`,\n To: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>`,\n \"Call-ID\": this.registerCallId,\n Contact: `<sip:${this.sipInfo.username}@${this.client.localAddress}:${this.client.localPort};transport=TLS;ob>;reg-id=1;+sip.instance=\"<urn:uuid:${this.instanceId}>\"`,\n Expires: 3600,\n Allow:\n \"PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS\",\n },\n );\n const inboundMessage = await this.send(requestMessage, true);\n if (inboundMessage.subject.startsWith(\"SIP/2.0 200 \")) {\n // sometimes the server will return 200 OK directly\n return;\n }\n if (!inboundMessage.subject.startsWith(\"SIP/2.0 401 \")) {\n throw new Error(`Failed to register: ${inboundMessage.subject}`);\n }\n const wwwAuth = inboundMessage.getHeader(\"Www-Authenticate\")!;\n const nonce = wwwAuth.match(/, nonce=\"(.+?)\"/)![1];\n const newMessage = requestMessage.fork();\n newMessage.headers.Authorization = generateAuthorization(\n this.sipInfo,\n nonce,\n \"REGISTER\",\n );\n const message = await this.send(newMessage, true);\n if (!message.subject.startsWith(\"SIP/2.0 200 \")) {\n throw new Error(`Failed to register: ${message.subject}`);\n }\n };\n await sipRegister();\n this.intervalHandle = setInterval(\n () => {\n sipRegister().catch((error) => {\n this.emit(\"registrationError\", error);\n });\n },\n 30 * 1000, // refresh registration every 30 seconds\n );\n this.on(\"message\", (inboundMessage) => {\n if (!inboundMessage.subject.startsWith(\"INVITE sip:\")) {\n return;\n }\n const outboundMessage = new OutboundMessage(\"SIP/2.0 100 Trying\", {\n Via: inboundMessage.headers.Via,\n \"Call-ID\": inboundMessage.getHeader(\"Call-ID\"),\n From: inboundMessage.headers.From,\n To: inboundMessage.headers.To,\n CSeq: inboundMessage.headers.CSeq,\n \"Content-Length\": \"0\",\n });\n this.send(outboundMessage);\n this.emit(\"invite\", inboundMessage);\n });\n }\n\n public enableDebugMode(\n options = {\n inboundPrefix: \"Receiving...\\n\",\n outboundPrefix: \"Sending...\\n\",\n },\n ) {\n this.on(\"message\", (message) => {\n console.log(\n `${options.inboundPrefix}(${new Date()})\\n${message.toString()}`,\n );\n });\n this.on(\"outboundMessage\", (message) => {\n console.log(`${options.outboundPrefix}(${new Date()})\\n${message}`);\n });\n }\n\n public revoke() {\n clearInterval(this.intervalHandle);\n this.removeAllListeners();\n this.client.removeAllListeners();\n this.client.destroy();\n }\n\n public send(\n message: OutboundMessage,\n waitForReply?: true,\n ): Promise<InboundMessage>;\n public send(\n message: OutboundMessage,\n waitForReply?: false,\n ): Promise<undefined>;\n public send(message: OutboundMessage, waitForReply = false) {\n this.client.write(message.toString());\n if (!waitForReply) {\n return Promise.resolve(undefined);\n }\n return new Promise<InboundMessage>((resolve) => {\n const messageListerner = (inboundMessage: InboundMessage) => {\n // \"12563 INVITE\" vs \"12563 ACK\"\n if (\n inboundMessage.headers.CSeq.trim().split(/\\s+/)[0] !==\n message.headers.CSeq.trim().split(/\\s+/)[0]\n ) {\n return;\n }\n if (inboundMessage.subject.startsWith(\"SIP/2.0 100 \")) {\n return; // ignore\n }\n this.off(\"message\", messageListerner);\n resolve(inboundMessage);\n };\n this.on(\"message\", messageListerner);\n });\n }\n\n public async answer(inviteMessage: InboundMessage) {\n const inboundCallSession = new InboundCallSession(this, inviteMessage);\n await inboundCallSession.answer();\n return inboundCallSession;\n }\n\n // decline an inbound call\n public async decline(inviteMessage: InboundMessage) {\n const newMessage = new ResponseMessage(inviteMessage, 603);\n await this.send(newMessage);\n }\n\n public async call(callee: string) {\n const { socket, port } = await CallSession.createBoundSocket();\n const offerSDP = `\nv=0\no=- ${Date.now()} 0 IN IP4 ${this.client.localAddress}\ns=rc-softphone-ts\nc=IN IP4 ${this.client.localAddress}\nt=0 0\nm=audio ${port} RTP/SAVP ${this.codec.id} 101\na=rtpmap:${this.codec.id} ${this.codec.name}\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-15\na=sendrecv\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}\n `.trim();\n const inviteMessage = new RequestMessage(\n `INVITE sip:${callee}@${this.sipInfo.domain} SIP/2.0`,\n {\n Via: `SIP/2.0/TLS ${this.client.localAddress}:${this.client.localPort};rport;branch=${branch()};alias`,\n \"Max-Forwards\": 70,\n From: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>;tag=${uuid()}`,\n To: `<sip:${callee}@${this.sipInfo.domain}>`,\n Contact: ` <sip:${this.sipInfo.username}@${this.client.localAddress}:${this.client.localPort};transport=TLS;ob>`,\n \"Call-ID\": uuid(),\n Route: `<sip:${this.sipInfo.outboundProxy};transport=tls;lr>`,\n Allow: `PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS`,\n Supported: `replaces, 100rel, timer, norefersub`,\n \"Session-Expires\": 1800,\n \"Min-SE\": 90,\n \"Content-Type\": \"application/sdp\",\n },\n offerSDP,\n );\n const inboundMessage = await this.send(inviteMessage, true);\n const proxyAuthenticate = inboundMessage.getHeader(\"Proxy-Authenticate\")!;\n const nonce = proxyAuthenticate.match(/, nonce=\"(.+?)\"/)![1];\n const newMessage = inviteMessage.fork();\n newMessage.headers[\"Proxy-Authorization\"] = generateAuthorization(\n this.sipInfo,\n nonce,\n \"INVITE\",\n );\n const progressMessage = await this.send(newMessage, true);\n const outboundCallSession = new OutboundCallSession(\n this,\n progressMessage,\n socket,\n );\n outboundCallSession.sdp = offerSDP;\n return outboundCallSession;\n }\n}\n\nexport default Softphone;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/sip-message/inbound/index.ts"],"sourcesContent":["import { uuid } from \"../../utils.js\";\nimport SipMessage from \"../sip-message.js\";\n\nclass InboundMessage extends SipMessage {\n public static fromString(str: string) {\n const sipMessage = new SipMessage();\n const [init, ...body] = str.split(\"\\r\\n\\r\\n\");\n sipMessage.body = body.join(\"\\r\\n\\r\\n\");\n const [subject, ...headers] = init.split(\"\\r\\n\");\n sipMessage.subject = subject;\n sipMessage.headers = Object.fromEntries(\n headers.map((line) => line.split(\": \")),\n );\n if (sipMessage.headers.To && !sipMessage.headers.To.includes(\";tag=\")) {\n sipMessage.headers.To += `;tag=${uuid()}`; // generate local tag\n }\n return sipMessage;\n }\n}\n\nexport default InboundMessage;\n"],"mappings":";;;AAGA,IAAM,iBAAN,cAA6B,WAAW;CACtC,OAAc,WAAW,KAAa;EACpC,MAAM,aAAa,IAAI,YAAY;EACnC,MAAM,CAAC,MAAM,GAAG,QAAQ,IAAI,MAAM,WAAW;AAC7C,aAAW,OAAO,KAAK,KAAK,WAAW;EACvC,MAAM,CAAC,SAAS,GAAG,WAAW,KAAK,MAAM,OAAO;AAChD,aAAW,UAAU;AACrB,aAAW,UAAU,OAAO,YAC1B,QAAQ,KAAK,SAAS,KAAK,MAAM,KAAK,CAAC,CACxC;AACD,MAAI,WAAW,QAAQ,MAAM,CAAC,WAAW,QAAQ,GAAG,SAAS,QAAQ,CACnE,YAAW,QAAQ,MAAM,QAAQ,MAAM;AAEzC,SAAO"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/sip-message/inbound/index.ts"],"sourcesContent":["import { uuid } from \"../../utils.js\";\nimport SipMessage from \"../sip-message.js\";\n\nclass InboundMessage extends SipMessage {\n public static fromString(str: string) {\n const sipMessage = new SipMessage();\n const [init, ...body] = str.split(\"\\r\\n\\r\\n\");\n sipMessage.body = body.join(\"\\r\\n\\r\\n\");\n const [subject, ...headers] = init.split(\"\\r\\n\");\n sipMessage.subject = subject;\n sipMessage.headers = Object.fromEntries(\n headers.map((line) => line.split(\": \")),\n );\n if (sipMessage.headers.To && !sipMessage.headers.To.includes(\";tag=\")) {\n sipMessage.headers.To += `;tag=${uuid()}`; // generate local tag\n }\n return sipMessage;\n }\n}\n\nexport default InboundMessage;\n"],"mappings":";;;AAGA,IAAM,iBAAN,cAA6B,WAAW;CACtC,OAAc,WAAW,KAAa;EACpC,MAAM,aAAa,IAAI,YAAY;EACnC,MAAM,CAAC,MAAM,GAAG,QAAQ,IAAI,MAAM,WAAW;EAC7C,WAAW,OAAO,KAAK,KAAK,WAAW;EACvC,MAAM,CAAC,SAAS,GAAG,WAAW,KAAK,MAAM,OAAO;EAChD,WAAW,UAAU;EACrB,WAAW,UAAU,OAAO,YAC1B,QAAQ,KAAK,SAAS,KAAK,MAAM,KAAK,CAAC,CACxC;EACD,IAAI,WAAW,QAAQ,MAAM,CAAC,WAAW,QAAQ,GAAG,SAAS,QAAQ,EACnE,WAAW,QAAQ,MAAM,QAAQ,MAAM;EAEzC,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/sip-message/outbound/index.ts"],"sourcesContent":["import SipMessage from \"../sip-message.js\";\n\nclass OutboundMessage extends SipMessage {\n public constructor(subject = \"\", headers = {}, body = \"\") {\n super(subject, headers, body);\n this.headers[\"Content-Length\"] = this.body.length.toString();\n this.headers[\"User-Agent\"] = \"ringcentral-softphone-ts\";\n }\n}\n\nexport default OutboundMessage;\n"],"mappings":";;AAEA,IAAM,kBAAN,cAA8B,WAAW;CACvC,YAAmB,UAAU,IAAI,UAAU,EAAE,EAAE,OAAO,IAAI;AACxD,QAAM,SAAS,SAAS,KAAK;AAC7B,OAAK,QAAQ,oBAAoB,KAAK,KAAK,OAAO,UAAU;AAC5D,OAAK,QAAQ,gBAAgB"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/sip-message/outbound/index.ts"],"sourcesContent":["import SipMessage from \"../sip-message.js\";\n\nclass OutboundMessage extends SipMessage {\n public constructor(subject = \"\", headers = {}, body = \"\") {\n super(subject, headers, body);\n this.headers[\"Content-Length\"] = this.body.length.toString();\n this.headers[\"User-Agent\"] = \"ringcentral-softphone-ts\";\n }\n}\n\nexport default OutboundMessage;\n"],"mappings":";;AAEA,IAAM,kBAAN,cAA8B,WAAW;CACvC,YAAmB,UAAU,IAAI,UAAU,EAAE,EAAE,OAAO,IAAI;EACxD,MAAM,SAAS,SAAS,KAAK;EAC7B,KAAK,QAAQ,oBAAoB,KAAK,KAAK,OAAO,UAAU;EAC5D,KAAK,QAAQ,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"request.mjs","names":[],"sources":["../../../src/sip-message/outbound/request.ts"],"sourcesContent":["import { branch } from \"../../utils.js\";\nimport OutboundMessage from \"./index.js\";\n\nlet cseq = Math.floor(Math.random() * 10000);\n\nclass RequestMessage extends OutboundMessage {\n public constructor(subject = \"\", headers = {}, body = \"\") {\n super(subject, headers, body);\n if (this.headers.CSeq === undefined) {\n this.newCseq();\n }\n }\n\n public newCseq() {\n this.headers.CSeq = `${++cseq} ${this.subject.split(\" \")[0]}`;\n }\n\n public fork() {\n const newMessage = new RequestMessage(\n this.subject,\n { ...this.headers },\n this.body,\n );\n newMessage.newCseq();\n if (newMessage.headers.Via) {\n newMessage.headers.Via = newMessage.headers.Via.replace(\n /;branch=.+?$/,\n `;branch=${branch()}`,\n );\n }\n return newMessage;\n }\n}\n\nexport default RequestMessage;\n"],"mappings":";;;AAGA,IAAI,OAAO,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAM;AAE5C,IAAM,iBAAN,MAAM,uBAAuB,gBAAgB;CAC3C,YAAmB,UAAU,IAAI,UAAU,EAAE,EAAE,OAAO,IAAI;AACxD,QAAM,SAAS,SAAS,KAAK;AAC7B,MAAI,KAAK,QAAQ,SAAS,KAAA,EACxB,MAAK,SAAS;;CAIlB,UAAiB;AACf,OAAK,QAAQ,OAAO,GAAG,EAAE,KAAK,GAAG,KAAK,QAAQ,MAAM,IAAI,CAAC;;CAG3D,OAAc;EACZ,MAAM,aAAa,IAAI,eACrB,KAAK,SACL,EAAE,GAAG,KAAK,SAAS,EACnB,KAAK,KACN;AACD,aAAW,SAAS;AACpB,MAAI,WAAW,QAAQ,IACrB,YAAW,QAAQ,MAAM,WAAW,QAAQ,IAAI,QAC9C,gBACA,WAAW,QAAQ,GACpB;AAEH,SAAO"}
1
+ {"version":3,"file":"request.mjs","names":[],"sources":["../../../src/sip-message/outbound/request.ts"],"sourcesContent":["import { branch } from \"../../utils.js\";\nimport OutboundMessage from \"./index.js\";\n\nlet cseq = Math.floor(Math.random() * 10000);\n\nclass RequestMessage extends OutboundMessage {\n public constructor(subject = \"\", headers = {}, body = \"\") {\n super(subject, headers, body);\n if (this.headers.CSeq === undefined) {\n this.newCseq();\n }\n }\n\n public newCseq() {\n this.headers.CSeq = `${++cseq} ${this.subject.split(\" \")[0]}`;\n }\n\n public fork() {\n const newMessage = new RequestMessage(\n this.subject,\n { ...this.headers },\n this.body,\n );\n newMessage.newCseq();\n if (newMessage.headers.Via) {\n newMessage.headers.Via = newMessage.headers.Via.replace(\n /;branch=.+?$/,\n `;branch=${branch()}`,\n );\n }\n return newMessage;\n }\n}\n\nexport default RequestMessage;\n"],"mappings":";;;AAGA,IAAI,OAAO,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAM;AAE5C,IAAM,iBAAN,MAAM,uBAAuB,gBAAgB;CAC3C,YAAmB,UAAU,IAAI,UAAU,EAAE,EAAE,OAAO,IAAI;EACxD,MAAM,SAAS,SAAS,KAAK;EAC7B,IAAI,KAAK,QAAQ,SAAS,KAAA,GACxB,KAAK,SAAS;;CAIlB,UAAiB;EACf,KAAK,QAAQ,OAAO,GAAG,EAAE,KAAK,GAAG,KAAK,QAAQ,MAAM,IAAI,CAAC;;CAG3D,OAAc;EACZ,MAAM,aAAa,IAAI,eACrB,KAAK,SACL,EAAE,GAAG,KAAK,SAAS,EACnB,KAAK,KACN;EACD,WAAW,SAAS;EACpB,IAAI,WAAW,QAAQ,KACrB,WAAW,QAAQ,MAAM,WAAW,QAAQ,IAAI,QAC9C,gBACA,WAAW,QAAQ,GACpB;EAEH,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"response.mjs","names":[],"sources":["../../../src/sip-message/outbound/response.ts"],"sourcesContent":["import type InboundMessage from \"../inbound/index.js\";\nimport responseCodes from \"../response-codes.js\";\nimport OutboundMessage from \"./index.js\";\n\nclass ResponseMessage extends OutboundMessage {\n public constructor(\n inboundMessage: InboundMessage,\n responseCode: number,\n headers = {},\n body = \"\",\n ) {\n super(undefined, { ...headers }, body);\n this.subject = `SIP/2.0 ${responseCode} ${responseCodes[responseCode]}`;\n const requiredKeys = new Set([\"via\", \"from\", \"to\", \"call-id\", \"cseq\"]);\n const allKeys = Object.keys(inboundMessage.headers).reduce(\n (acc, key) => {\n acc[key.toLowerCase()] = key;\n return acc;\n },\n {} as Record<string, string>,\n );\n for (const key of requiredKeys) {\n if (allKeys[key]) {\n const originalKey = allKeys[key];\n this.headers[originalKey] = inboundMessage.headers[originalKey];\n }\n }\n }\n}\n\nexport default ResponseMessage;\n"],"mappings":";;;AAIA,IAAM,kBAAN,cAA8B,gBAAgB;CAC5C,YACE,gBACA,cACA,UAAU,EAAE,EACZ,OAAO,IACP;AACA,QAAM,KAAA,GAAW,EAAE,GAAG,SAAS,EAAE,KAAK;AACtC,OAAK,UAAU,WAAW,aAAa,GAAG,cAAc;EACxD,MAAM,eAAe,IAAI,IAAI;GAAC;GAAO;GAAQ;GAAM;GAAW;GAAO,CAAC;EACtE,MAAM,UAAU,OAAO,KAAK,eAAe,QAAQ,CAAC,QACjD,KAAK,QAAQ;AACZ,OAAI,IAAI,aAAa,IAAI;AACzB,UAAO;KAET,EAAE,CACH;AACD,OAAK,MAAM,OAAO,aAChB,KAAI,QAAQ,MAAM;GAChB,MAAM,cAAc,QAAQ;AAC5B,QAAK,QAAQ,eAAe,eAAe,QAAQ"}
1
+ {"version":3,"file":"response.mjs","names":[],"sources":["../../../src/sip-message/outbound/response.ts"],"sourcesContent":["import type InboundMessage from \"../inbound/index.js\";\nimport responseCodes from \"../response-codes.js\";\nimport OutboundMessage from \"./index.js\";\n\nclass ResponseMessage extends OutboundMessage {\n public constructor(\n inboundMessage: InboundMessage,\n responseCode: number,\n headers = {},\n body = \"\",\n ) {\n super(undefined, { ...headers }, body);\n this.subject = `SIP/2.0 ${responseCode} ${responseCodes[responseCode]}`;\n const requiredKeys = new Set([\"via\", \"from\", \"to\", \"call-id\", \"cseq\"]);\n const allKeys = Object.keys(inboundMessage.headers).reduce(\n (acc, key) => {\n acc[key.toLowerCase()] = key;\n return acc;\n },\n {} as Record<string, string>,\n );\n for (const key of requiredKeys) {\n if (allKeys[key]) {\n const originalKey = allKeys[key];\n this.headers[originalKey] = inboundMessage.headers[originalKey];\n }\n }\n }\n}\n\nexport default ResponseMessage;\n"],"mappings":";;;AAIA,IAAM,kBAAN,cAA8B,gBAAgB;CAC5C,YACE,gBACA,cACA,UAAU,EAAE,EACZ,OAAO,IACP;EACA,MAAM,KAAA,GAAW,EAAE,GAAG,SAAS,EAAE,KAAK;EACtC,KAAK,UAAU,WAAW,aAAa,GAAG,cAAc;EACxD,MAAM,eAAe,IAAI,IAAI;GAAC;GAAO;GAAQ;GAAM;GAAW;GAAO,CAAC;EACtE,MAAM,UAAU,OAAO,KAAK,eAAe,QAAQ,CAAC,QACjD,KAAK,QAAQ;GACZ,IAAI,IAAI,aAAa,IAAI;GACzB,OAAO;KAET,EAAE,CACH;EACD,KAAK,MAAM,OAAO,cAChB,IAAI,QAAQ,MAAM;GAChB,MAAM,cAAc,QAAQ;GAC5B,KAAK,QAAQ,eAAe,eAAe,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"sip-message.mjs","names":[],"sources":["../../src/sip-message/sip-message.ts"],"sourcesContent":["class SipMessage {\n public subject: string;\n public headers: {\n [key: string]: string;\n };\n public body: string;\n\n public constructor(subject = \"\", headers = {}, body = \"\") {\n this.subject = subject;\n this.headers = headers;\n this.body = body\n .trim()\n .split(/[\\r\\n]+/)\n .join(\"\\r\\n\");\n if (this.body.length > 0) {\n this.body += \"\\r\\n\";\n }\n }\n\n public toString() {\n const r = [\n this.subject,\n ...Object.keys(this.headers).map((key) => `${key}: ${this.headers[key]}`),\n \"\",\n this.body,\n ].join(\"\\r\\n\");\n return r;\n }\n\n public getHeader(key: string): string | undefined {\n const foundKey = Object.keys(this.headers).find(\n (k) => k.toLowerCase() === key.toLowerCase(),\n );\n if (foundKey) {\n return this.headers[foundKey];\n }\n }\n}\n\nexport default SipMessage;\n"],"mappings":";AAAA,IAAM,aAAN,MAAiB;CACf;CACA;CAGA;CAEA,YAAmB,UAAU,IAAI,UAAU,EAAE,EAAE,OAAO,IAAI;AACxD,OAAK,UAAU;AACf,OAAK,UAAU;AACf,OAAK,OAAO,KACT,MAAM,CACN,MAAM,UAAU,CAChB,KAAK,OAAO;AACf,MAAI,KAAK,KAAK,SAAS,EACrB,MAAK,QAAQ;;CAIjB,WAAkB;AAOhB,SANU;GACR,KAAK;GACL,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,KAAK,QAAQ,GAAG,IAAI,IAAI,KAAK,QAAQ,OAAO;GACzE;GACA,KAAK;GACN,CAAC,KAAK,OACC;;CAGV,UAAiB,KAAiC;EAChD,MAAM,WAAW,OAAO,KAAK,KAAK,QAAQ,CAAC,MACxC,MAAM,EAAE,aAAa,KAAK,IAAI,aAAa,CAC7C;AACD,MAAI,SACF,QAAO,KAAK,QAAQ"}
1
+ {"version":3,"file":"sip-message.mjs","names":[],"sources":["../../src/sip-message/sip-message.ts"],"sourcesContent":["class SipMessage {\n public subject: string;\n public headers: {\n [key: string]: string;\n };\n public body: string;\n\n public constructor(subject = \"\", headers = {}, body = \"\") {\n this.subject = subject;\n this.headers = headers;\n this.body = body\n .trim()\n .split(/[\\r\\n]+/)\n .join(\"\\r\\n\");\n if (this.body.length > 0) {\n this.body += \"\\r\\n\";\n }\n }\n\n public toString() {\n const r = [\n this.subject,\n ...Object.keys(this.headers).map((key) => `${key}: ${this.headers[key]}`),\n \"\",\n this.body,\n ].join(\"\\r\\n\");\n return r;\n }\n\n public getHeader(key: string): string | undefined {\n const foundKey = Object.keys(this.headers).find(\n (k) => k.toLowerCase() === key.toLowerCase(),\n );\n if (foundKey) {\n return this.headers[foundKey];\n }\n }\n}\n\nexport default SipMessage;\n"],"mappings":";AAAA,IAAM,aAAN,MAAiB;CACf;CACA;CAGA;CAEA,YAAmB,UAAU,IAAI,UAAU,EAAE,EAAE,OAAO,IAAI;EACxD,KAAK,UAAU;EACf,KAAK,UAAU;EACf,KAAK,OAAO,KACT,MAAM,CACN,MAAM,UAAU,CAChB,KAAK,OAAO;EACf,IAAI,KAAK,KAAK,SAAS,GACrB,KAAK,QAAQ;;CAIjB,WAAkB;EAOhB,OANU;GACR,KAAK;GACL,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,KAAK,QAAQ,GAAG,IAAI,IAAI,KAAK,QAAQ,OAAO;GACzE;GACA,KAAK;GACN,CAAC,KAAK,OACC;;CAGV,UAAiB,KAAiC;EAChD,MAAM,WAAW,OAAO,KAAK,KAAK,QAAQ,CAAC,MACxC,MAAM,EAAE,aAAa,KAAK,IAAI,aAAa,CAC7C;EACD,IAAI,UACF,OAAO,KAAK,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import crypto from \"node:crypto\";\n\nimport type { SoftPhoneOptions } from \"./types.js\";\n\nconst md5 = (s: string) => crypto.createHash(\"md5\").update(s).digest(\"hex\");\n\nconst generateResponse = (\n sipInfo: SoftPhoneOptions,\n endpoint: string,\n nonce: string,\n) => {\n const ha1 = md5(\n `${sipInfo.authorizationId}:${sipInfo.domain}:${sipInfo.password}`,\n );\n const ha2 = md5(endpoint);\n const response = md5(`${ha1}:${nonce}:${ha2}`);\n return response;\n};\n\nexport const generateAuthorization = (\n sipInfo: SoftPhoneOptions,\n nonce: string,\n method: \"REGISTER\" | \"INVITE\",\n) => {\n const authObj = {\n \"Digest algorithm\": \"MD5\",\n username: sipInfo.authorizationId,\n realm: sipInfo.domain,\n nonce,\n uri: `sip:${sipInfo.domain}`,\n response: generateResponse(\n sipInfo,\n `${method}:sip:${sipInfo.domain}`,\n nonce,\n ),\n };\n return Object.entries(authObj)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(\", \");\n};\n\nexport const uuid = () => crypto.randomUUID();\nexport const branch = () => `z9hG4bK-${uuid()}`;\n\nexport const randomInt = () =>\n Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024;\n\nexport const withoutTag = (s: string) => s.replace(/;tag=.*$/, \"\");\nexport const extractAddress = (s: string) => s.match(/<(sip:.+?)>/)?.[1];\n\nconst keyAndSalt = crypto.randomBytes(30);\nexport const localKey = keyAndSalt.toString(\"base64\").replace(/=+$/, \"\");\n"],"mappings":";;AAIA,MAAM,OAAO,MAAc,OAAO,WAAW,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,MAAM;AAE3E,MAAM,oBACJ,SACA,UACA,UACG;AAMH,QADiB,IAAI,GAJT,IACV,GAAG,QAAQ,gBAAgB,GAAG,QAAQ,OAAO,GAAG,QAAQ,WAG/B,CAAC,GAAG,MAAM,GADzB,IAAI,SAC2B,GAC5B;;AAGjB,MAAa,yBACX,SACA,OACA,WACG;CACH,MAAM,UAAU;EACd,oBAAoB;EACpB,UAAU,QAAQ;EAClB,OAAO,QAAQ;EACf;EACA,KAAK,OAAO,QAAQ;EACpB,UAAU,iBACR,SACA,GAAG,OAAO,OAAO,QAAQ,UACzB,MACD;EACF;AACD,QAAO,OAAO,QAAQ,QAAQ,CAC3B,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,IAAI,MAAM,GAAG,CAC1C,KAAK,KAAK;;AAGf,MAAa,aAAa,OAAO,YAAY;AAC7C,MAAa,eAAe,WAAW,MAAM;AAE7C,MAAa,kBACX,KAAK,MAAM,KAAK,QAAQ,GAAI,MAAkB,GAAG;AAEnD,MAAa,cAAc,MAAc,EAAE,QAAQ,YAAY,GAAG;AAClE,MAAa,kBAAkB,MAAc,EAAE,MAAM,cAAc,GAAG;AAGtE,MAAa,WADM,OAAO,YAAY,GACd,CAAW,SAAS,SAAS,CAAC,QAAQ,OAAO,GAAG"}
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import crypto from \"node:crypto\";\n\nimport type { SoftPhoneOptions } from \"./types.js\";\n\nconst md5 = (s: string) => crypto.createHash(\"md5\").update(s).digest(\"hex\");\n\nconst generateResponse = (\n sipInfo: SoftPhoneOptions,\n endpoint: string,\n nonce: string,\n) => {\n const ha1 = md5(\n `${sipInfo.authorizationId}:${sipInfo.domain}:${sipInfo.password}`,\n );\n const ha2 = md5(endpoint);\n const response = md5(`${ha1}:${nonce}:${ha2}`);\n return response;\n};\n\nexport const generateAuthorization = (\n sipInfo: SoftPhoneOptions,\n nonce: string,\n method: \"REGISTER\" | \"INVITE\",\n) => {\n const authObj = {\n \"Digest algorithm\": \"MD5\",\n username: sipInfo.authorizationId,\n realm: sipInfo.domain,\n nonce,\n uri: `sip:${sipInfo.domain}`,\n response: generateResponse(\n sipInfo,\n `${method}:sip:${sipInfo.domain}`,\n nonce,\n ),\n };\n return Object.entries(authObj)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(\", \");\n};\n\nexport const uuid = () => crypto.randomUUID();\nexport const branch = () => `z9hG4bK-${uuid()}`;\n\nexport const randomInt = () =>\n Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024;\n\nexport const withoutTag = (s: string) => s.replace(/;tag=.*$/, \"\");\nexport const extractAddress = (s: string) => s.match(/<(sip:.+?)>/)?.[1];\n\nconst keyAndSalt = crypto.randomBytes(30);\nexport const localKey = keyAndSalt.toString(\"base64\").replace(/=+$/, \"\");\n"],"mappings":";;AAIA,MAAM,OAAO,MAAc,OAAO,WAAW,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,MAAM;AAE3E,MAAM,oBACJ,SACA,UACA,UACG;CAMH,OADiB,IAAI,GAJT,IACV,GAAG,QAAQ,gBAAgB,GAAG,QAAQ,OAAO,GAAG,QAAQ,WAG/B,CAAC,GAAG,MAAM,GADzB,IAAI,SAC2B,GAC5B;;AAGjB,MAAa,yBACX,SACA,OACA,WACG;CACH,MAAM,UAAU;EACd,oBAAoB;EACpB,UAAU,QAAQ;EAClB,OAAO,QAAQ;EACf;EACA,KAAK,OAAO,QAAQ;EACpB,UAAU,iBACR,SACA,GAAG,OAAO,OAAO,QAAQ,UACzB,MACD;EACF;CACD,OAAO,OAAO,QAAQ,QAAQ,CAC3B,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,IAAI,MAAM,GAAG,CAC1C,KAAK,KAAK;;AAGf,MAAa,aAAa,OAAO,YAAY;AAC7C,MAAa,eAAe,WAAW,MAAM;AAE7C,MAAa,kBACX,KAAK,MAAM,KAAK,QAAQ,GAAI,MAAkB,GAAG;AAEnD,MAAa,cAAc,MAAc,EAAE,QAAQ,YAAY,GAAG;AAClE,MAAa,kBAAkB,MAAc,EAAE,MAAM,cAAc,GAAG;AAGtE,MAAa,WADM,OAAO,YAAY,GACd,CAAW,SAAS,SAAS,CAAC,QAAQ,OAAO,GAAG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ringcentral-softphone",
3
- "version": "1.3.7",
3
+ "version": "1.3.9",
4
4
  "homepage": "https://github.com/ringcentral/ringcentral-softphone-ts",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,8 +28,11 @@
28
28
  "in": "rm -rf *.wav && tsx -r dotenv-override-true/config demos/inbound-call.ts",
29
29
  "out": "rm -rf *.wav && tsx -r dotenv-override-true/config demos/outbound-call.ts",
30
30
  "join": "rm -rf *.wav && tsx -r dotenv-override-true/config demos/join-rcv-meeting.ts",
31
- "multi": "tsx -r dotenv-override-true/config demos/multiple-calls-sequentially.ts",
31
+ "multi-call": "tsx -r dotenv-override-true/config demos/multiple-calls-sequentially.ts",
32
+ "multi-inst": "tsx -r dotenv-override-true/config demos/multi-instances.ts",
32
33
  "lint": "biome check --write .",
34
+ "test": "vitest run",
35
+ "typecheck:tests": "tsc -p tests/tsconfig.json",
33
36
  "build": "tsdown",
34
37
  "prepublishOnly": "yarn build",
35
38
  "postpublish": "rm -rf dist"
@@ -41,12 +44,13 @@
41
44
  "werift-rtp": "^0.8.8"
42
45
  },
43
46
  "devDependencies": {
44
- "@biomejs/biome": "^2.4.12",
45
- "@types/node": "^25.6.0",
47
+ "@biomejs/biome": "^2.4.15",
48
+ "@types/node": "^25.8.0",
46
49
  "dotenv-override-true": "^6.2.2",
47
- "tsdown": "^0.21.10",
48
- "tsx": "^4.21.0",
50
+ "tsdown": "^0.22.0",
51
+ "tsx": "^4.22.0",
49
52
  "typescript": "^6.0.3",
53
+ "vitest": "^4.1.6",
50
54
  "yarn-upgrade-all": "^0.8.1"
51
55
  },
52
56
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
@@ -1 +0,0 @@
1
- {"version":3,"file":"inbound--wGoGqLS.d.mts","names":[],"sources":["../src/call-session/streamer.ts","../src/call-session/index.ts","../src/call-session/outbound.ts","../src/index.ts","../src/call-session/inbound.ts"],"mappings":";;;;;;;;;;;;cAOM,QAAA,SAAiB,YAAA;EACd,MAAA;EAAA,QACC,WAAA;EAAA,QACA,MAAA;EAAA,QACA,cAAA;cAEW,WAAA,EAAa,WAAA,EAAa,MAAA,EAAQ,MAAA;EAO9C,KAAA,CAAA;EAMA,IAAA,CAAA;EAIA,KAAA,CAAA;EAIA,MAAA,CAAA;EAAA,IAKI,QAAA,CAAA;EAAA,QAOH,UAAA;AAAA;;;KC/BL,QAAA,WAAmB,IAAA,CAAK,UAAA;AAAA,uBAKd,WAAA,SAAoB,YAAA;EAC1B,SAAA,EAAW,SAAA;EACX,UAAA,EAAY,cAAA;EACZ,MAAA,EAAS,KAAA,CAAM,MAAA;EACf,SAAA;EACA,UAAA;EACA,QAAA;EACA,UAAA;EACA,QAAA;EACA,WAAA,EAAc,WAAA;EACd,OAAA;IAAW,MAAA,GAAS,GAAA,EAAK,MAAA,KAAW,MAAA;EAAA;EACpC,OAAA;IAAW,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,MAAA;EAAA;EACtC,GAAA;EAGA,IAAA;EACA,cAAA;EACA,SAAA;cAEY,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,cAAA;EAAA,IAgB1C,SAAA,CAAU,GAAA;EAAA,IAcV,MAAA,CAAA;EAIJ,IAAA,CAAK,IAAA,WAAe,MAAA;EAId,MAAA,CAAA,GAAM,OAAA;EAaZ,QAAA,CAAS,IAAA,EAAM,QAAA;EA8BT,SAAA,CAAU,CAAA,UAAW,KAAA,YAAW,OAAA;EAYtC,WAAA,CAAY,KAAA,EAAO,MAAA,GAAM,QAAA;EAOzB,UAAA,CAAW,SAAA,EAAW,SAAA;EAAA,UAOnB,kBAAA,CAAA;EAAA,UAsDA,OAAA,CAAA;EAQG,QAAA,CAAS,UAAA,WAAkB,OAAA;EAqC3B,aAAA,CAAc,SAAA,YAAkB,OAAA;EA+BhC,IAAA,CAAA,GAAI,OAAA;EAIJ,MAAA,CAAA,GAAM,OAAA;AAAA;;;cCnRf,mBAAA,SAA4B,WAAA;cACb,SAAA,EAAW,SAAA,EAAW,aAAA,EAAe,cAAA;EAUjD,IAAA,CAAA;EAiCM,MAAA,CAAA,GAAM,OAAA;EAAA,IAcR,SAAA,CAAA;EAAA,IAKA,OAAA,CAAA;AAAA;;;cC7CP,SAAA,SAAkB,YAAA;EACf,OAAA,EAAS,gBAAA;EACT,MAAA,EAAQ,SAAA;EACR,KAAA,EAAO,KAAA;EAEP,UAAA;EACA,SAAA;EAAA,QAEC,cAAA;EAAA,QACA,SAAA;cAEW,OAAA,EAAS,gBAAA;EAAA,QA+CpB,UAAA;EAAA,QACA,cAAA;EAEK,QAAA,CAAA,GAAQ,OAAA;EAuEd,eAAA,CAAA;EAWA,MAAA,CAAA;EAOA,IAAA,CACL,OAAA,EAAS,eAAA,EACT,YAAA,UACC,OAAA,CAAQ,cAAA;EACJ,IAAA,CACL,OAAA,EAAS,eAAA,EACT,YAAA,WACC,OAAA;EAyBU,MAAA,CAAO,aAAA,EAAe,cAAA,GAAc,OAAA,CAAA,kBAAA;EAOpC,OAAA,CAAQ,aAAA,EAAe,cAAA,GAAc,OAAA;EAKrC,IAAA,CAAK,MAAA,WAAc,OAAA,CAAA,mBAAA;AAAA;;;cCpN5B,kBAAA,SAA2B,WAAA;cACZ,SAAA,EAAW,SAAA,EAAW,aAAA,EAAe,cAAA;EAY3C,MAAA,CAAA,GAAM,OAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"inbound-DquvTSj1.d.cts","names":[],"sources":["../src/call-session/streamer.ts","../src/call-session/index.ts","../src/call-session/outbound.ts","../src/index.ts","../src/call-session/inbound.ts"],"mappings":";;;;;;;;;;;;cAOM,QAAA,SAAiB,YAAA;EACd,MAAA;EAAA,QACC,WAAA;EAAA,QACA,MAAA;EAAA,QACA,cAAA;cAEW,WAAA,EAAa,WAAA,EAAa,MAAA,EAAQ,MAAA;EAO9C,KAAA,CAAA;EAMA,IAAA,CAAA;EAIA,KAAA,CAAA;EAIA,MAAA,CAAA;EAAA,IAKI,QAAA,CAAA;EAAA,QAOH,UAAA;AAAA;;;KC/BL,QAAA,WAAmB,IAAA,CAAK,UAAA;AAAA,uBAKd,WAAA,SAAoB,YAAA;EAC1B,SAAA,EAAW,SAAA;EACX,UAAA,EAAY,cAAA;EACZ,MAAA,EAAS,KAAA,CAAM,MAAA;EACf,SAAA;EACA,UAAA;EACA,QAAA;EACA,UAAA;EACA,QAAA;EACA,WAAA,EAAc,WAAA;EACd,OAAA;IAAW,MAAA,GAAS,GAAA,EAAK,MAAA,KAAW,MAAA;EAAA;EACpC,OAAA;IAAW,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,MAAA;EAAA;EACtC,GAAA;EAGA,IAAA;EACA,cAAA;EACA,SAAA;cAEY,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,cAAA;EAAA,IAgB1C,SAAA,CAAU,GAAA;EAAA,IAcV,MAAA,CAAA;EAIJ,IAAA,CAAK,IAAA,WAAe,MAAA;EAId,MAAA,CAAA,GAAM,OAAA;EAaZ,QAAA,CAAS,IAAA,EAAM,QAAA;EA8BT,SAAA,CAAU,CAAA,UAAW,KAAA,YAAW,OAAA;EAYtC,WAAA,CAAY,KAAA,EAAO,MAAA,GAAM,QAAA;EAOzB,UAAA,CAAW,SAAA,EAAW,SAAA;EAAA,UAOnB,kBAAA,CAAA;EAAA,UAsDA,OAAA,CAAA;EAQG,QAAA,CAAS,UAAA,WAAkB,OAAA;EAqC3B,aAAA,CAAc,SAAA,YAAkB,OAAA;EA+BhC,IAAA,CAAA,GAAI,OAAA;EAIJ,MAAA,CAAA,GAAM,OAAA;AAAA;;;cCnRf,mBAAA,SAA4B,WAAA;cACb,SAAA,EAAW,SAAA,EAAW,aAAA,EAAe,cAAA;EAUjD,IAAA,CAAA;EAiCM,MAAA,CAAA,GAAM,OAAA;EAAA,IAcR,SAAA,CAAA;EAAA,IAKA,OAAA,CAAA;AAAA;;;cC7CP,SAAA,SAAkB,YAAA;EACf,OAAA,EAAS,gBAAA;EACT,MAAA,EAAQ,SAAA;EACR,KAAA,EAAO,KAAA;EAEP,UAAA;EACA,SAAA;EAAA,QAEC,cAAA;EAAA,QACA,SAAA;cAEW,OAAA,EAAS,gBAAA;EAAA,QA+CpB,UAAA;EAAA,QACA,cAAA;EAEK,QAAA,CAAA,GAAQ,OAAA;EAuEd,eAAA,CAAA;EAWA,MAAA,CAAA;EAOA,IAAA,CACL,OAAA,EAAS,eAAA,EACT,YAAA,UACC,OAAA,CAAQ,cAAA;EACJ,IAAA,CACL,OAAA,EAAS,eAAA,EACT,YAAA,WACC,OAAA;EAyBU,MAAA,CAAO,aAAA,EAAe,cAAA,GAAc,OAAA,CAAA,kBAAA;EAOpC,OAAA,CAAQ,aAAA,EAAe,cAAA,GAAc,OAAA;EAKrC,IAAA,CAAK,MAAA,WAAc,OAAA,CAAA,mBAAA;AAAA;;;cCpN5B,kBAAA,SAA2B,WAAA;cACZ,SAAA,EAAW,SAAA,EAAW,aAAA,EAAe,cAAA;EAY3C,MAAA,CAAA,GAAM,OAAA;AAAA"}