ringcentral-softphone 1.3.6 → 1.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -4
- package/dist/call-session/inbound.cjs +3 -1
- package/dist/call-session/inbound.d.cts +1 -1
- package/dist/call-session/inbound.d.mts +1 -1
- package/dist/call-session/inbound.mjs +6 -2
- package/dist/call-session/inbound.mjs.map +1 -0
- package/dist/call-session/index.cjs +21 -2
- package/dist/call-session/index.d.cts +1 -1
- package/dist/call-session/index.d.mts +1 -1
- package/dist/call-session/index.mjs +23 -2
- package/dist/call-session/index.mjs.map +1 -0
- package/dist/call-session/outbound.cjs +2 -1
- package/dist/call-session/outbound.d.cts +1 -1
- package/dist/call-session/outbound.d.mts +1 -1
- package/dist/call-session/outbound.mjs +4 -1
- package/dist/call-session/outbound.mjs.map +1 -0
- package/dist/call-session/streamer.d.cts +1 -1
- package/dist/call-session/streamer.d.mts +1 -1
- package/dist/call-session/streamer.mjs +2 -0
- package/dist/call-session/streamer.mjs.map +1 -0
- package/dist/codec-Bh7v8J-S.d.mts +2 -1
- package/dist/codec-Bh7v8J-S.d.mts.map +1 -0
- package/dist/codec-C-VrtVkq.d.cts +2 -1
- package/dist/codec-C-VrtVkq.d.cts.map +1 -0
- package/dist/codec.mjs +2 -0
- package/dist/codec.mjs.map +1 -0
- package/dist/dtmf-B13Fz2VR.d.mts +2 -1
- package/dist/dtmf-B13Fz2VR.d.mts.map +1 -0
- package/dist/dtmf-DcQ-5vSG.d.cts +2 -1
- package/dist/dtmf-DcQ-5vSG.d.cts.map +1 -0
- package/dist/dtmf.mjs +2 -0
- package/dist/dtmf.mjs.map +1 -0
- package/dist/{inbound-DquvTSj1.d.cts → inbound-Dg-_UGyn.d.cts} +8 -3
- package/dist/inbound-Dg-_UGyn.d.cts.map +1 -0
- package/dist/{inbound--wGoGqLS.d.mts → inbound-DzyKzVVF.d.mts} +7 -2
- package/dist/inbound-DzyKzVVF.d.mts.map +1 -0
- package/dist/index--UjWgLK-.d.mts +2 -1
- package/dist/index--UjWgLK-.d.mts.map +1 -0
- package/dist/index-BhN2W8AV.d.mts +2 -1
- package/dist/index-BhN2W8AV.d.mts.map +1 -0
- package/dist/index-Cf2Cev52.d.cts +2 -1
- package/dist/index-Cf2Cev52.d.cts.map +1 -0
- package/dist/index-XMDop59x.d.cts +2 -1
- package/dist/index-XMDop59x.d.cts.map +1 -0
- package/dist/index.cjs +11 -6
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +14 -7
- package/dist/index.mjs.map +1 -0
- package/dist/request-B_auLSJn.d.cts +2 -1
- package/dist/request-B_auLSJn.d.cts.map +1 -0
- package/dist/request-pBe7_mYv.d.mts +2 -1
- package/dist/request-pBe7_mYv.d.mts.map +1 -0
- package/dist/response-LRRpY8lX.d.mts +2 -1
- package/dist/response-LRRpY8lX.d.mts.map +1 -0
- package/dist/response-ReKvb5x9.d.cts +2 -1
- package/dist/response-ReKvb5x9.d.cts.map +1 -0
- package/dist/sip-message/inbound/index.mjs +2 -0
- package/dist/sip-message/inbound/index.mjs.map +1 -0
- package/dist/sip-message/outbound/index.mjs +2 -0
- package/dist/sip-message/outbound/index.mjs.map +1 -0
- package/dist/sip-message/outbound/request.mjs +2 -0
- package/dist/sip-message/outbound/request.mjs.map +1 -0
- package/dist/sip-message/outbound/response.mjs +2 -0
- package/dist/sip-message/outbound/response.mjs.map +1 -0
- package/dist/sip-message/response-codes.d.cts +2 -1
- package/dist/sip-message/response-codes.d.cts.map +1 -0
- package/dist/sip-message/response-codes.d.mts +2 -1
- package/dist/sip-message/response-codes.d.mts.map +1 -0
- package/dist/sip-message/response-codes.mjs +2 -0
- package/dist/sip-message/response-codes.mjs.map +1 -0
- package/dist/sip-message/sip-message.mjs +2 -0
- package/dist/sip-message/sip-message.mjs.map +1 -0
- package/dist/sip-message-B2D5MPBI.d.cts +2 -1
- package/dist/sip-message-B2D5MPBI.d.cts.map +1 -0
- package/dist/sip-message-PaPho4qU.d.mts +2 -1
- package/dist/sip-message-PaPho4qU.d.mts.map +1 -0
- package/dist/types-DOQ9wmX6.d.mts +2 -1
- package/dist/types-DOQ9wmX6.d.mts.map +1 -0
- package/dist/types-DZxCsbZE.d.cts +2 -1
- package/dist/types-DZxCsbZE.d.cts.map +1 -0
- package/dist/utils.d.cts +2 -1
- package/dist/utils.d.cts.map +1 -0
- package/dist/utils.d.mts +2 -1
- package/dist/utils.d.mts.map +1 -0
- package/dist/utils.mjs +2 -0
- package/dist/utils.mjs.map +1 -0
- package/package.json +7 -4
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
|
-
##
|
|
12
|
+
## More documentation
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
More documentation is available here:
|
|
15
15
|
https://ringcentral.github.io/ringcentral-softphone-ts/
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
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 ${
|
|
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-
|
|
1
|
+
import { t as InboundCallSession } from "../inbound-Dg-_UGyn.cjs";
|
|
2
2
|
export = InboundCallSession;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as InboundCallSession } from "../inbound
|
|
1
|
+
import { t as InboundCallSession } from "../inbound-DzyKzVVF.mjs";
|
|
2
2
|
export { InboundCallSession as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { localKey
|
|
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 ${
|
|
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
|
|
@@ -49,3 +51,5 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}
|
|
|
49
51
|
};
|
|
50
52
|
//#endregion
|
|
51
53
|
export { InboundCallSession as default };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=inbound.mjs.map
|
|
@@ -0,0 +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 } 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
|
|
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-
|
|
1
|
+
import { i as CallSession } from "../inbound-Dg-_UGyn.cjs";
|
|
2
2
|
export = CallSession;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as CallSession } from "../inbound
|
|
1
|
+
import { i as CallSession } from "../inbound-DzyKzVVF.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
|
|
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;
|
|
@@ -207,3 +226,5 @@ var CallSession = class extends EventEmitter {
|
|
|
207
226
|
};
|
|
208
227
|
//#endregion
|
|
209
228
|
export { CallSession as default };
|
|
229
|
+
|
|
230
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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 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-
|
|
1
|
+
import { r as OutboundCallSession } from "../inbound-Dg-_UGyn.cjs";
|
|
2
2
|
export = OutboundCallSession;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { r as OutboundCallSession } from "../inbound
|
|
1
|
+
import { r as OutboundCallSession } from "../inbound-DzyKzVVF.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];
|
|
@@ -55,3 +56,5 @@ var OutboundCallSession = class extends CallSession {
|
|
|
55
56
|
};
|
|
56
57
|
//#endregion
|
|
57
58
|
export { OutboundCallSession as default };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=outbound.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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-
|
|
1
|
+
import { a as Streamer } from "../inbound-Dg-_UGyn.cjs";
|
|
2
2
|
export = Streamer;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as Streamer } from "../inbound
|
|
1
|
+
import { a as Streamer } from "../inbound-DzyKzVVF.mjs";
|
|
2
2
|
export { Streamer as default };
|
|
@@ -0,0 +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;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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec-Bh7v8J-S.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;cAGM,KAAA;EACJ,EAAA;EACA,IAAA;EACA,UAAA;EACA,iBAAA;EACA,aAAA;IAAuB,MAAA,GAAS,GAAA,EAAK,MAAA,KAAW,MAAA;EAAA;EAChD,aAAA;IAAuB,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,MAAA;EAAA;cACtC,IAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec-C-VrtVkq.d.cts","names":[],"sources":["../src/codec.ts"],"mappings":";;;cAGM,KAAA;EACJ,EAAA;EACA,IAAA;EACA,UAAA;EACA,iBAAA;EACA,aAAA;IAAuB,MAAA,GAAS,GAAA,EAAK,MAAA,KAAW,MAAA;EAAA;EAChD,aAAA;IAAuB,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,MAAA;EAAA;cACtC,IAAA;AAAA"}
|
package/dist/codec.mjs
CHANGED
|
@@ -0,0 +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;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-B13Fz2VR.d.mts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dtmf-B13Fz2VR.d.mts","names":[],"sources":["../src/dtmf.ts"],"mappings":";;;cAoBM,IAAA;;gCAEuB,MAAA,CAAA,WAAA;yBAYJ,MAAA;AAAA"}
|
package/dist/dtmf-DcQ-5vSG.d.cts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dtmf-DcQ-5vSG.d.cts","names":[],"sources":["../src/dtmf.ts"],"mappings":";;;cAoBM,IAAA;;gCAEuB,MAAA,CAAA,WAAA;yBAYJ,MAAA;AAAA"}
|
package/dist/dtmf.mjs
CHANGED
|
@@ -0,0 +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;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"}
|
|
@@ -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;
|
|
@@ -100,4 +104,5 @@ declare class InboundCallSession extends CallSession {
|
|
|
100
104
|
answer(): Promise<void>;
|
|
101
105
|
}
|
|
102
106
|
//#endregion
|
|
103
|
-
export { Streamer as a, CallSession as i, Softphone as n, OutboundCallSession as r, InboundCallSession as t };
|
|
107
|
+
export { Streamer as a, CallSession as i, Softphone as n, OutboundCallSession as r, InboundCallSession as t };
|
|
108
|
+
//# sourceMappingURL=inbound-Dg-_UGyn.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound-Dg-_UGyn.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;EAuEd,eAAA,CAAA;EASA,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;;;cClN5B,kBAAA,SAA2B,WAAA;cACZ,SAAA,EAAW,SAAA,EAAW,aAAA,EAAe,cAAA;EAY3C,MAAA,CAAA,GAAM,OAAA;AAAA"}
|
|
@@ -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;
|
|
@@ -100,4 +104,5 @@ declare class InboundCallSession extends CallSession {
|
|
|
100
104
|
answer(): Promise<void>;
|
|
101
105
|
}
|
|
102
106
|
//#endregion
|
|
103
|
-
export { Streamer as a, CallSession as i, Softphone as n, OutboundCallSession as r, InboundCallSession as t };
|
|
107
|
+
export { Streamer as a, CallSession as i, Softphone as n, OutboundCallSession as r, InboundCallSession as t };
|
|
108
|
+
//# sourceMappingURL=inbound-DzyKzVVF.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound-DzyKzVVF.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;EAuEd,eAAA,CAAA;EASA,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;;;cClN5B,kBAAA,SAA2B,WAAA;cACZ,SAAA,EAAW,SAAA,EAAW,aAAA,EAAe,cAAA;EAY3C,MAAA,CAAA,GAAM,OAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index--UjWgLK-.d.mts","names":[],"sources":["../src/sip-message/outbound/index.ts"],"mappings":";;;cAEM,eAAA,SAAwB,UAAA;cACT,OAAA,WAAc,OAAA,OAAc,IAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-BhN2W8AV.d.mts","names":[],"sources":["../src/sip-message/inbound/index.ts"],"mappings":";;;cAGM,cAAA,SAAuB,UAAA;EAAA,OACb,UAAA,CAAW,GAAA,WAAW,UAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Cf2Cev52.d.cts","names":[],"sources":["../src/sip-message/inbound/index.ts"],"mappings":";;;cAGM,cAAA,SAAuB,UAAA;EAAA,OACb,UAAA,CAAW,GAAA,WAAW,UAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-XMDop59x.d.cts","names":[],"sources":["../src/sip-message/outbound/index.ts"],"mappings":";;;cAEM,eAAA,SAAwB,UAAA;cACT,OAAA,WAAc,OAAA,OAAc,IAAA;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");
|
|
@@ -101,11 +107,9 @@ var Softphone = class extends node_events.default {
|
|
|
101
107
|
}
|
|
102
108
|
enableDebugMode() {
|
|
103
109
|
this.on("message", (message) => console.log(`Receiving...(${/* @__PURE__ */ new Date()})\n${message.toString()}`));
|
|
104
|
-
|
|
105
|
-
this.client.write = (message) => {
|
|
110
|
+
this.on("outboundMessage", (message) => {
|
|
106
111
|
console.log(`Sending...(${/* @__PURE__ */ new Date()})\n${message}`);
|
|
107
|
-
|
|
108
|
-
};
|
|
112
|
+
});
|
|
109
113
|
}
|
|
110
114
|
revoke() {
|
|
111
115
|
clearInterval(this.intervalHandle);
|
|
@@ -136,13 +140,14 @@ var Softphone = class extends node_events.default {
|
|
|
136
140
|
await this.send(newMessage);
|
|
137
141
|
}
|
|
138
142
|
async call(callee) {
|
|
143
|
+
const { socket, port } = await require_call_session_index.createBoundSocket();
|
|
139
144
|
const offerSDP = `
|
|
140
145
|
v=0
|
|
141
146
|
o=- ${Date.now()} 0 IN IP4 ${this.client.localAddress}
|
|
142
147
|
s=rc-softphone-ts
|
|
143
148
|
c=IN IP4 ${this.client.localAddress}
|
|
144
149
|
t=0 0
|
|
145
|
-
m=audio ${
|
|
150
|
+
m=audio ${port} RTP/SAVP ${this.codec.id} 101
|
|
146
151
|
a=rtpmap:${this.codec.id} ${this.codec.name}
|
|
147
152
|
a=rtpmap:101 telephone-event/8000
|
|
148
153
|
a=fmtp:101 0-15
|
|
@@ -167,7 +172,7 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${require_utils.localKey}
|
|
|
167
172
|
const newMessage = inviteMessage.fork();
|
|
168
173
|
newMessage.headers["Proxy-Authorization"] = require_utils.generateAuthorization(this.sipInfo, nonce, "INVITE");
|
|
169
174
|
const progressMessage = await this.send(newMessage, true);
|
|
170
|
-
const outboundCallSession = new require_call_session_outbound(this, progressMessage);
|
|
175
|
+
const outboundCallSession = new require_call_session_outbound(this, progressMessage, socket);
|
|
171
176
|
outboundCallSession.sdp = offerSDP;
|
|
172
177
|
return outboundCallSession;
|
|
173
178
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as Softphone } from "./inbound-
|
|
1
|
+
import { n as Softphone } from "./inbound-Dg-_UGyn.cjs";
|
|
2
2
|
export = Softphone;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as Softphone } from "./inbound
|
|
1
|
+
import { n as Softphone } from "./inbound-DzyKzVVF.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,
|
|
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");
|
|
@@ -97,11 +103,9 @@ var Softphone = class extends EventEmitter {
|
|
|
97
103
|
}
|
|
98
104
|
enableDebugMode() {
|
|
99
105
|
this.on("message", (message) => console.log(`Receiving...(${/* @__PURE__ */ new Date()})\n${message.toString()}`));
|
|
100
|
-
|
|
101
|
-
this.client.write = (message) => {
|
|
106
|
+
this.on("outboundMessage", (message) => {
|
|
102
107
|
console.log(`Sending...(${/* @__PURE__ */ new Date()})\n${message}`);
|
|
103
|
-
|
|
104
|
-
};
|
|
108
|
+
});
|
|
105
109
|
}
|
|
106
110
|
revoke() {
|
|
107
111
|
clearInterval(this.intervalHandle);
|
|
@@ -132,13 +136,14 @@ var Softphone = class extends EventEmitter {
|
|
|
132
136
|
await this.send(newMessage);
|
|
133
137
|
}
|
|
134
138
|
async call(callee) {
|
|
139
|
+
const { socket, port } = await CallSession.createBoundSocket();
|
|
135
140
|
const offerSDP = `
|
|
136
141
|
v=0
|
|
137
142
|
o=- ${Date.now()} 0 IN IP4 ${this.client.localAddress}
|
|
138
143
|
s=rc-softphone-ts
|
|
139
144
|
c=IN IP4 ${this.client.localAddress}
|
|
140
145
|
t=0 0
|
|
141
|
-
m=audio ${
|
|
146
|
+
m=audio ${port} RTP/SAVP ${this.codec.id} 101
|
|
142
147
|
a=rtpmap:${this.codec.id} ${this.codec.name}
|
|
143
148
|
a=rtpmap:101 telephone-event/8000
|
|
144
149
|
a=fmtp:101 0-15
|
|
@@ -163,10 +168,12 @@ a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}
|
|
|
163
168
|
const newMessage = inviteMessage.fork();
|
|
164
169
|
newMessage.headers["Proxy-Authorization"] = generateAuthorization(this.sipInfo, nonce, "INVITE");
|
|
165
170
|
const progressMessage = await this.send(newMessage, true);
|
|
166
|
-
const outboundCallSession = new OutboundCallSession(this, progressMessage);
|
|
171
|
+
const outboundCallSession = new OutboundCallSession(this, progressMessage, socket);
|
|
167
172
|
outboundCallSession.sdp = offerSDP;
|
|
168
173
|
return outboundCallSession;
|
|
169
174
|
}
|
|
170
175
|
};
|
|
171
176
|
//#endregion
|
|
172
177
|
export { Softphone as default };
|
|
178
|
+
|
|
179
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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 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 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 this.on(\"outboundMessage\", (message) => {\n console.log(`Sending...(${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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-B_auLSJn.d.cts","names":[],"sources":["../src/sip-message/outbound/request.ts"],"mappings":";;;cAKM,cAAA,SAAuB,eAAA;cACR,OAAA,WAAc,OAAA,OAAc,IAAA;EAOxC,OAAA,CAAA;EAIA,IAAA,CAAA,GAAI,cAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-pBe7_mYv.d.mts","names":[],"sources":["../src/sip-message/outbound/request.ts"],"mappings":";;;cAKM,cAAA,SAAuB,eAAA;cACR,OAAA,WAAc,OAAA,OAAc,IAAA;EAOxC,OAAA,CAAA;EAIA,IAAA,CAAA,GAAI,cAAA;AAAA"}
|
|
@@ -6,4 +6,5 @@ declare class ResponseMessage extends OutboundMessage {
|
|
|
6
6
|
constructor(inboundMessage: InboundMessage, responseCode: number, headers?: {}, body?: string);
|
|
7
7
|
}
|
|
8
8
|
//#endregion
|
|
9
|
-
export { ResponseMessage as t };
|
|
9
|
+
export { ResponseMessage as t };
|
|
10
|
+
//# sourceMappingURL=response-LRRpY8lX.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-LRRpY8lX.d.mts","names":[],"sources":["../src/sip-message/outbound/response.ts"],"mappings":";;;;cAIM,eAAA,SAAwB,eAAA;cAE1B,cAAA,EAAgB,cAAA,EAChB,YAAA,UACA,OAAA,OACA,IAAA;AAAA"}
|
|
@@ -6,4 +6,5 @@ declare class ResponseMessage extends OutboundMessage {
|
|
|
6
6
|
constructor(inboundMessage: InboundMessage, responseCode: number, headers?: {}, body?: string);
|
|
7
7
|
}
|
|
8
8
|
//#endregion
|
|
9
|
-
export { ResponseMessage as t };
|
|
9
|
+
export { ResponseMessage as t };
|
|
10
|
+
//# sourceMappingURL=response-ReKvb5x9.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-ReKvb5x9.d.cts","names":[],"sources":["../src/sip-message/outbound/response.ts"],"mappings":";;;;cAIM,eAAA,SAAwB,eAAA;cAE1B,cAAA,EAAgB,cAAA,EAChB,YAAA,UACA,OAAA,OACA,IAAA;AAAA"}
|
|
@@ -0,0 +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;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"}
|
|
@@ -0,0 +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;EACxD,MAAM,SAAS,SAAS,KAAK;EAC7B,KAAK,QAAQ,oBAAoB,KAAK,KAAK,OAAO,UAAU;EAC5D,KAAK,QAAQ,gBAAgB"}
|
|
@@ -0,0 +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;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"}
|
|
@@ -0,0 +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;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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-codes.d.cts","names":[],"sources":["../../src/sip-message/response-codes.ts"],"mappings":";cACM,aAAA;EAAA,CACH,GAAA;AAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-codes.d.mts","names":[],"sources":["../../src/sip-message/response-codes.ts"],"mappings":";cACM,aAAA;EAAA,CACH,GAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-codes.mjs","names":[],"sources":["../../src/sip-message/response-codes.ts"],"sourcesContent":["// Ref: https://en.wikipedia.org/wiki/List_of_SIP_response_codes'\nconst responseCodes: {\n [key: number]: string;\n} = {\n 100: \"Trying\",\n 180: \"Ringing\",\n 181: \"Call is Being Forwarded\",\n 182: \"Queued\",\n 183: \"Session Progress\",\n 199: \"Early Dialog Terminated\",\n 200: \"OK\",\n 202: \"Accepted\",\n 204: \"No Notification\",\n 300: \"Multiple Choices\",\n 301: \"Moved Permanently\",\n 302: \"Moved Temporarily\",\n 305: \"Use Proxy\",\n 380: \"Alternative Service\",\n 400: \"Bad Request\",\n 401: \"Unauthorized\",\n 402: \"Payment Required\",\n 403: \"Forbidden\",\n 404: \"Not Found\",\n 405: \"Method Not Allowed\",\n 406: \"Not Acceptable\",\n 407: \"Proxy Authentication Required\",\n 408: \"Request Timeout\",\n 409: \"Conflict\",\n 410: \"Gone\",\n 411: \"Length Required\",\n 412: \"Conditional Request Failed\",\n 413: \"Request Entity Too Large\",\n 414: \"Request-URI Too Long\",\n 415: \"Unsupported Media Type\",\n 416: \"Unsupported URI Scheme\",\n 417: \"Unknown Resource-Priority\",\n 420: \"Bad Extension\",\n 421: \"Extension Required\",\n 422: \"Session Interval Too Small\",\n 423: \"Interval Too Brief\",\n 424: \"Bad Location Information\",\n 425: \"Bad Alert Message\",\n 428: \"Use Identity Header\",\n 429: \"Provide Referrer Identity\",\n 430: \"Flow Failed\",\n 433: \"Anonymity Disallowed\",\n 436: \"Bad Identity-Info\",\n 437: \"Unsupported Certificate\",\n 438: \"Invalid Identity Header\",\n 439: \"First Hop Lacks Outbound Support\",\n 440: \"Max-Breadth Exceeded\",\n 469: \"Bad Info Package\",\n 470: \"Consent Needed\",\n 480: \"Temporarily Unavailable\",\n 481: \"Call/Transaction Does Not Exist\",\n 482: \"Loop Detected\",\n 483: \"Too Many Hops\",\n 484: \"Address Incomplete\",\n 485: \"Ambiguous\",\n 486: \"Busy Here\",\n 487: \"Request Terminated\",\n 488: \"Not Acceptable Here\",\n 489: \"Bad Event\",\n 491: \"Request Pending\",\n 493: \"Undecipherable\",\n 494: \"Security Agreement Required\",\n 500: \"Server Internal Error\",\n 501: \"Not Implemented\",\n 502: \"Bad Gateway\",\n 503: \"Service Unavailable\",\n 504: \"Server Time-out\",\n 505: \"Version Not Supported\",\n 513: \"Message Too Large\",\n 555: \"Push Notification Service Not Supported\",\n 580: \"Precondition Failure\",\n 600: \"Busy Everywhere\",\n 603: \"Decline\",\n 604: \"Does Not Exist Anywhere\",\n 606: \"Not Acceptable\",\n 607: \"Unwanted\",\n 608: \"Rejected\",\n};\n\nexport default responseCodes;\n"],"mappings":";AACA,MAAM,gBAEF;CACF,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN"}
|
|
@@ -0,0 +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;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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sip-message-B2D5MPBI.d.cts","names":[],"sources":["../src/sip-message/sip-message.ts"],"mappings":";cAAM,UAAA;EACG,OAAA;EACA,OAAA;IAAA,CACJ,GAAA;EAAA;EAEI,IAAA;cAEY,OAAA,WAAc,OAAA,OAAc,IAAA;EAYxC,QAAA,CAAA;EAUA,SAAA,CAAU,GAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sip-message-PaPho4qU.d.mts","names":[],"sources":["../src/sip-message/sip-message.ts"],"mappings":";cAAM,UAAA;EACG,OAAA;EACA,OAAA;IAAA,CACJ,GAAA;EAAA;EAEI,IAAA;cAEY,OAAA,WAAc,OAAA,OAAc,IAAA;EAYxC,QAAA,CAAA;EAUA,SAAA,CAAU,GAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DOQ9wmX6.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";KAAY,gBAAA;EACV,MAAA;EACA,aAAA;EACA,QAAA;EACA,QAAA;EACA,eAAA;EACA,KAAA;EACA,mBAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DZxCsbZE.d.cts","names":[],"sources":["../src/types.ts"],"mappings":";KAAY,gBAAA;EACV,MAAA;EACA,aAAA;EACA,QAAA;EACA,QAAA;EACA,eAAA;EACA,KAAA;EACA,mBAAA;AAAA"}
|
package/dist/utils.d.cts
CHANGED
|
@@ -9,4 +9,5 @@ declare const withoutTag: (s: string) => string;
|
|
|
9
9
|
declare const extractAddress: (s: string) => string | undefined;
|
|
10
10
|
declare const localKey: string;
|
|
11
11
|
//#endregion
|
|
12
|
-
export { branch, extractAddress, generateAuthorization, localKey, randomInt, uuid, withoutTag };
|
|
12
|
+
export { branch, extractAddress, generateAuthorization, localKey, randomInt, uuid, withoutTag };
|
|
13
|
+
//# sourceMappingURL=utils.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.cts","names":[],"sources":["../src/utils.ts"],"mappings":";;;cAmBa,qBAAA,GACX,OAAA,EAAS,gBAAA,EACT,KAAA,UACA,MAAA;AAAA,cAmBW,IAAA;AAAA,cACA,MAAA;AAAA,cAEA,SAAA;AAAA,cAGA,UAAA,GAAc,CAAA;AAAA,cACd,cAAA,GAAkB,CAAA;AAAA,cAGlB,QAAA"}
|
package/dist/utils.d.mts
CHANGED
|
@@ -9,4 +9,5 @@ declare const withoutTag: (s: string) => string;
|
|
|
9
9
|
declare const extractAddress: (s: string) => string | undefined;
|
|
10
10
|
declare const localKey: string;
|
|
11
11
|
//#endregion
|
|
12
|
-
export { branch, extractAddress, generateAuthorization, localKey, randomInt, uuid, withoutTag };
|
|
12
|
+
export { branch, extractAddress, generateAuthorization, localKey, randomInt, uuid, withoutTag };
|
|
13
|
+
//# sourceMappingURL=utils.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.mts","names":[],"sources":["../src/utils.ts"],"mappings":";;;cAmBa,qBAAA,GACX,OAAA,EAAS,gBAAA,EACT,KAAA,UACA,MAAA;AAAA,cAmBW,IAAA;AAAA,cACA,MAAA;AAAA,cAEA,SAAA;AAAA,cAGA,UAAA,GAAc,CAAA;AAAA,cACd,cAAA,GAAkB,CAAA;AAAA,cAGlB,QAAA"}
|
package/dist/utils.mjs
CHANGED
|
@@ -23,3 +23,5 @@ const extractAddress = (s) => s.match(/<(sip:.+?)>/)?.[1];
|
|
|
23
23
|
const localKey = crypto.randomBytes(30).toString("base64").replace(/=+$/, "");
|
|
24
24
|
//#endregion
|
|
25
25
|
export { branch, extractAddress, generateAuthorization, localKey, randomInt, uuid, withoutTag };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +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;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.
|
|
3
|
+
"version": "1.3.8",
|
|
4
4
|
"homepage": "https://github.com/ringcentral/ringcentral-softphone-ts",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"join": "rm -rf *.wav && tsx -r dotenv-override-true/config demos/join-rcv-meeting.ts",
|
|
31
31
|
"multi": "tsx -r dotenv-override-true/config demos/multiple-calls-sequentially.ts",
|
|
32
32
|
"lint": "biome check --write .",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"typecheck:tests": "tsc -p tests/tsconfig.json",
|
|
33
35
|
"build": "tsdown",
|
|
34
36
|
"prepublishOnly": "yarn build",
|
|
35
37
|
"postpublish": "rm -rf dist"
|
|
@@ -41,12 +43,13 @@
|
|
|
41
43
|
"werift-rtp": "^0.8.8"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
|
-
"@biomejs/biome": "^2.4.
|
|
45
|
-
"@types/node": "^25.
|
|
46
|
+
"@biomejs/biome": "^2.4.15",
|
|
47
|
+
"@types/node": "^25.7.0",
|
|
46
48
|
"dotenv-override-true": "^6.2.2",
|
|
47
|
-
"tsdown": "^0.
|
|
49
|
+
"tsdown": "^0.22.0",
|
|
48
50
|
"tsx": "^4.21.0",
|
|
49
51
|
"typescript": "^6.0.3",
|
|
52
|
+
"vitest": "^4.1.6",
|
|
50
53
|
"yarn-upgrade-all": "^0.8.1"
|
|
51
54
|
},
|
|
52
55
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|