@fonoster/apiserver 0.7.23 → 0.7.25
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/dist/applications/createGetFnUtil.d.ts +6 -6
- package/dist/applications/deleteApplication.js +1 -1
- package/dist/applications/getApplication.js +1 -1
- package/dist/applications/utils/getApplicationValidationSchema.d.ts +6 -6
- package/dist/applications/utils/getApplicationValidationSchema.js +3 -1
- package/dist/core/allowList.js +3 -0
- package/dist/core/{filesServer.d.ts → httpBridge.d.ts} +2 -2
- package/dist/core/{filesServer.js → httpBridge.js} +26 -4
- package/dist/core/identityConfig.d.ts +9 -0
- package/dist/core/identityConfig.js +9 -0
- package/dist/core/runServices.js +2 -2
- package/dist/core/seed.js +10 -0
- package/dist/core/services.d.ts +9 -0
- package/dist/envs.d.ts +8 -2
- package/dist/envs.js +21 -9
- package/dist/secrets/createGetFnUtil.d.ts +2 -2
- package/dist/secrets/createGetFnUtil.js +1 -1
- package/dist/secrets/deleteSecret.js +1 -1
- package/dist/secrets/getSecret.js +1 -1
- package/dist/voice/handlers/PlaybackControl.js +4 -2
- package/dist/voice/handlers/Say.js +3 -3
- package/dist/voice/handlers/Stream.js +6 -2
- package/dist/voice/handlers/StreamGather.js +3 -1
- package/dist/voice/handlers/dial/Dial.js +4 -3
- package/dist/voice/handlers/dial/handleStasisStart.js +1 -0
- package/dist/voice/handlers/gather/Gather.js +15 -4
- package/dist/voice/handlers/utils/textChunksByClause.d.ts +2 -0
- package/dist/voice/handlers/utils/textChunksByClause.js +42 -0
- package/dist/voice/handlers/utils/withErrorHandling.js +4 -2
- package/dist/voice/integrations/makeCreateContainer.js +1 -1
- package/dist/voice/stt/Deepgram.js +8 -2
- package/dist/voice/stt/Google.js +3 -1
- package/dist/voice/tts/Azure.d.ts +2 -8
- package/dist/voice/tts/Azure.js +15 -24
- package/dist/voice/tts/Deepgram.d.ts +3 -2
- package/dist/voice/tts/Deepgram.js +60 -7
- package/dist/voice/tts/ElevenLabs.js +3 -1
- package/dist/voice/tts/Google.js +5 -4
- package/dist/voice/tts/streamToBuffer.d.ts +2 -0
- package/dist/voice/tts/streamToBuffer.js +61 -0
- package/package.json +6 -6
|
@@ -6,28 +6,28 @@ declare function createGetFnUtil(prisma: Prisma): (ref: string) => Promise<{
|
|
|
6
6
|
textToSpeech: {
|
|
7
7
|
ref: string;
|
|
8
8
|
config: import("@prisma/client/runtime/library").JsonValue;
|
|
9
|
-
applicationRef: string;
|
|
10
9
|
productRef: string;
|
|
10
|
+
applicationRef: string;
|
|
11
11
|
};
|
|
12
12
|
speechToText: {
|
|
13
13
|
ref: string;
|
|
14
14
|
config: import("@prisma/client/runtime/library").JsonValue;
|
|
15
|
-
applicationRef: string;
|
|
16
15
|
productRef: string;
|
|
16
|
+
applicationRef: string;
|
|
17
17
|
};
|
|
18
18
|
intelligence: {
|
|
19
19
|
ref: string;
|
|
20
|
-
credentials: string;
|
|
21
20
|
config: import("@prisma/client/runtime/library").JsonValue;
|
|
22
|
-
|
|
21
|
+
credentials: string;
|
|
23
22
|
productRef: string;
|
|
23
|
+
applicationRef: string;
|
|
24
24
|
};
|
|
25
25
|
name: string;
|
|
26
26
|
type: "EXTERNAL";
|
|
27
|
+
endpoint: string;
|
|
27
28
|
ref: string;
|
|
29
|
+
accessKeyId: string;
|
|
28
30
|
createdAt: Date;
|
|
29
31
|
updatedAt: Date;
|
|
30
|
-
endpoint: string;
|
|
31
|
-
accessKeyId: string;
|
|
32
32
|
}>;
|
|
33
33
|
export { createGetFnUtil };
|
|
@@ -41,5 +41,5 @@ function deleteApplication(prisma) {
|
|
|
41
41
|
yield prisma.application.delete({ where: { ref } });
|
|
42
42
|
return { ref };
|
|
43
43
|
});
|
|
44
|
-
return (0, common_1.withErrorHandlingAndValidation)((0, identity_1.withAccess)(fn, (ref) => getFn(ref)), common_1.Validators.
|
|
44
|
+
return (0, common_1.withErrorHandlingAndValidation)((0, identity_1.withAccess)(fn, (ref) => getFn(ref)), common_1.Validators.emptySchema);
|
|
45
45
|
}
|
|
@@ -42,5 +42,5 @@ function getApplication(prisma) {
|
|
|
42
42
|
const result = yield getFn(ref);
|
|
43
43
|
return result ? (0, applicationWithEncodedStruct_1.applicationWithEncodedStruct)(result) : null;
|
|
44
44
|
});
|
|
45
|
-
return (0, withErrorHandlingAndValidationAndAccess_1.withErrorHandlingAndValidationAndAccess)(fn, (ref) => getFn(ref), common_1.Validators.
|
|
45
|
+
return (0, withErrorHandlingAndValidationAndAccess_1.withErrorHandlingAndValidationAndAccess)(fn, (ref) => getFn(ref), common_1.Validators.emptySchema);
|
|
46
46
|
}
|
|
@@ -8,7 +8,7 @@ declare function getApplicationValidationSchema(request: {
|
|
|
8
8
|
EXTERNAL: "EXTERNAL";
|
|
9
9
|
}>;
|
|
10
10
|
endpoint: z.ZodEffects<z.ZodOptional<z.ZodString>, string, string>;
|
|
11
|
-
textToSpeech: z.
|
|
11
|
+
textToSpeech: z.ZodObject<{
|
|
12
12
|
productRef: z.ZodString;
|
|
13
13
|
config: any;
|
|
14
14
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -19,7 +19,7 @@ declare function getApplicationValidationSchema(request: {
|
|
|
19
19
|
[x: string]: any;
|
|
20
20
|
productRef?: unknown;
|
|
21
21
|
config?: unknown;
|
|
22
|
-
}
|
|
22
|
+
}> | z.ZodUndefined;
|
|
23
23
|
speechToText: z.ZodUndefined | z.ZodObject<{
|
|
24
24
|
productRef: z.ZodString;
|
|
25
25
|
config: any;
|
|
@@ -33,9 +33,6 @@ declare function getApplicationValidationSchema(request: {
|
|
|
33
33
|
config?: unknown;
|
|
34
34
|
}>;
|
|
35
35
|
}, "strip", z.ZodTypeAny, {
|
|
36
|
-
name?: string;
|
|
37
|
-
type?: "EXTERNAL";
|
|
38
|
-
endpoint?: string;
|
|
39
36
|
textToSpeech?: {
|
|
40
37
|
[x: string]: any;
|
|
41
38
|
productRef?: unknown;
|
|
@@ -46,10 +43,10 @@ declare function getApplicationValidationSchema(request: {
|
|
|
46
43
|
productRef?: unknown;
|
|
47
44
|
config?: unknown;
|
|
48
45
|
};
|
|
49
|
-
}, {
|
|
50
46
|
name?: string;
|
|
51
47
|
type?: "EXTERNAL";
|
|
52
48
|
endpoint?: string;
|
|
49
|
+
}, {
|
|
53
50
|
textToSpeech?: {
|
|
54
51
|
[x: string]: any;
|
|
55
52
|
productRef?: unknown;
|
|
@@ -60,5 +57,8 @@ declare function getApplicationValidationSchema(request: {
|
|
|
60
57
|
productRef?: unknown;
|
|
61
58
|
config?: unknown;
|
|
62
59
|
};
|
|
60
|
+
name?: string;
|
|
61
|
+
type?: "EXTERNAL";
|
|
62
|
+
endpoint?: string;
|
|
63
63
|
}>;
|
|
64
64
|
export { getApplicationValidationSchema };
|
|
@@ -55,7 +55,9 @@ function getApplicationValidationSchema(request) {
|
|
|
55
55
|
const { ttsEngineName, sttEngineName } = request;
|
|
56
56
|
return zod_1.z.object({
|
|
57
57
|
name: zod_1.z.string().max(255, MAX_NAME_MESSAGE),
|
|
58
|
-
type: zod_1.z.nativeEnum(client_1.ApplicationType
|
|
58
|
+
type: zod_1.z.nativeEnum(client_1.ApplicationType, {
|
|
59
|
+
message: "Invalid application type."
|
|
60
|
+
}),
|
|
59
61
|
endpoint: common_1.hostOrHostPortSchema,
|
|
60
62
|
textToSpeech: ttsEngineName
|
|
61
63
|
? zod_1.z.object({
|
package/dist/core/allowList.js
CHANGED
|
@@ -25,7 +25,10 @@ const allowList = [
|
|
|
25
25
|
"/fonoster.identity.v1beta2.Identity/CreateWorkspace",
|
|
26
26
|
"/fonoster.identity.v1beta2.Identity/ExchangeApiKey",
|
|
27
27
|
"/fonoster.identity.v1beta2.Identity/ExchangeCredentials",
|
|
28
|
+
"/fonoster.identity.v1beta2.Identity/ExchangeOauth2Code",
|
|
28
29
|
"/fonoster.identity.v1beta2.Identity/ExchangeRefreshToken",
|
|
30
|
+
"/fonoster.identity.v1beta2.Identity/SendVerificationCode",
|
|
31
|
+
"/fonoster.identity.v1beta2.Identity/VerifyCode",
|
|
29
32
|
"/fonoster.identity.v1beta2.Identity/GetPublicKey"
|
|
30
33
|
];
|
|
31
34
|
exports.allowList = allowList;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Readable } from "stream";
|
|
2
|
-
declare function
|
|
2
|
+
declare function httpBridge(params: {
|
|
3
3
|
port: number;
|
|
4
4
|
}): {
|
|
5
5
|
addStream: (id: string, stream: Readable) => void;
|
|
6
6
|
removeStream: (id: string) => void;
|
|
7
7
|
getStream: (id: string) => Readable;
|
|
8
8
|
};
|
|
9
|
-
export {
|
|
9
|
+
export { httpBridge };
|
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
13
|
};
|
|
5
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
15
|
+
exports.httpBridge = httpBridge;
|
|
16
|
+
const identity_1 = require("@fonoster/identity");
|
|
7
17
|
const logger_1 = require("@fonoster/logger");
|
|
8
18
|
const express_1 = __importDefault(require("express"));
|
|
19
|
+
const identityConfig_1 = require("./identityConfig");
|
|
20
|
+
const envs_1 = require("../envs");
|
|
9
21
|
const logger = (0, logger_1.getLogger)({ service: "apiserver", filePath: __filename });
|
|
10
22
|
const CONTENT_TYPE = "audio/L16;rate=16000;channels=1";
|
|
11
|
-
function
|
|
23
|
+
function httpBridge(params) {
|
|
12
24
|
const { port } = params;
|
|
13
25
|
const app = (0, express_1.default)();
|
|
14
26
|
const streamMap = new Map();
|
|
15
|
-
app.get("/sounds/:id", (req, res) => {
|
|
27
|
+
app.get("/api/sounds/:id", (req, res) => {
|
|
16
28
|
const idWithoutExtension = req.params.id.split(".")[0];
|
|
17
29
|
const stream = streamMap.get(idWithoutExtension);
|
|
18
30
|
if (!stream) {
|
|
@@ -32,8 +44,18 @@ function filesServer(params) {
|
|
|
32
44
|
});
|
|
33
45
|
stream.pipe(res);
|
|
34
46
|
});
|
|
47
|
+
app.get("/api/identity/accept-invite", (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
try {
|
|
49
|
+
yield (0, identity_1.updateMembershipStatus)(identityConfig_1.identityConfig)(req.query.token);
|
|
50
|
+
res.redirect(envs_1.APP_URL);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
logger.verbose("error updating membership status", error);
|
|
54
|
+
res.redirect(envs_1.IDENTITY_WORKSPACE_INVITATION_FAIL_URL);
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
35
57
|
app.listen(port, () => {
|
|
36
|
-
logger.info(`
|
|
58
|
+
logger.info(`HTTP bridge is running on port ${port}`);
|
|
37
59
|
});
|
|
38
60
|
return {
|
|
39
61
|
addStream: (id, stream) => {
|
|
@@ -17,5 +17,14 @@ declare const identityConfig: {
|
|
|
17
17
|
pass: string;
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
|
+
twilioSmsConfig: {
|
|
21
|
+
accountSid: string;
|
|
22
|
+
authToken: string;
|
|
23
|
+
sender: string;
|
|
24
|
+
};
|
|
25
|
+
githubOauth2Config: {
|
|
26
|
+
clientId: string;
|
|
27
|
+
clientSecret: string;
|
|
28
|
+
};
|
|
20
29
|
};
|
|
21
30
|
export { identityConfig };
|
|
@@ -38,6 +38,15 @@ const identityConfig = {
|
|
|
38
38
|
user: envs_1.SMTP_AUTH_USER,
|
|
39
39
|
pass: envs_1.SMTP_AUTH_PASS
|
|
40
40
|
}
|
|
41
|
+
},
|
|
42
|
+
twilioSmsConfig: {
|
|
43
|
+
accountSid: envs_1.TWILIO_ACCOUNT_SID,
|
|
44
|
+
authToken: envs_1.TWILIO_AUTH_TOKEN,
|
|
45
|
+
sender: envs_1.TWILIO_PHONE_NUMBER
|
|
46
|
+
},
|
|
47
|
+
githubOauth2Config: {
|
|
48
|
+
clientId: envs_1.IDENTITY_OAUTH2_GITHUB_CLIENT_ID,
|
|
49
|
+
clientSecret: envs_1.IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET
|
|
41
50
|
}
|
|
42
51
|
};
|
|
43
52
|
exports.identityConfig = identityConfig;
|
package/dist/core/runServices.js
CHANGED
|
@@ -59,7 +59,7 @@ const logger_1 = require("@fonoster/logger");
|
|
|
59
59
|
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
60
60
|
const grpc_health_check_1 = require("grpc-health-check");
|
|
61
61
|
const allowList_1 = require("./allowList");
|
|
62
|
-
const
|
|
62
|
+
const httpBridge_1 = require("./httpBridge");
|
|
63
63
|
const loadServices_1 = __importDefault(require("./loadServices"));
|
|
64
64
|
const services_1 = __importDefault(require("./services"));
|
|
65
65
|
const runCallManager_1 = require("../calls/runCallManager");
|
|
@@ -79,7 +79,7 @@ function runServices() {
|
|
|
79
79
|
// Load the remaining services
|
|
80
80
|
(0, loadServices_1.default)(server, yield services_1.default);
|
|
81
81
|
// Connecting to Asterisk ARI
|
|
82
|
-
yield (0, connectToAri_1.connectToAri)((0,
|
|
82
|
+
yield (0, connectToAri_1.connectToAri)((0, httpBridge_1.httpBridge)({ port: envs_1.HTTP_BRIDGE_PORT }));
|
|
83
83
|
// Additional Call Managers subscriber may be added here to handle call events
|
|
84
84
|
yield (0, runCallManager_1.createCreateCallSubscriber)({
|
|
85
85
|
natsUrl: envs_1.NATS_URL,
|
package/dist/core/seed.js
CHANGED
|
@@ -93,6 +93,16 @@ function main() {
|
|
|
93
93
|
type: "ASSISTANT"
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
|
+
yield prisma.product.upsert({
|
|
97
|
+
where: { ref: "tts.azure" },
|
|
98
|
+
update: {},
|
|
99
|
+
create: {
|
|
100
|
+
ref: "tts.azure",
|
|
101
|
+
name: "Azure Text-to-Speech",
|
|
102
|
+
vendor: "MICROSOFT",
|
|
103
|
+
type: "TTS"
|
|
104
|
+
}
|
|
105
|
+
});
|
|
96
106
|
});
|
|
97
107
|
}
|
|
98
108
|
main()
|
package/dist/core/services.d.ts
CHANGED
|
@@ -129,12 +129,21 @@ declare const services: Promise<[{
|
|
|
129
129
|
exchangeCredentials: (call: {
|
|
130
130
|
request: unknown;
|
|
131
131
|
}, callback: (error?: import("@fonoster/common").GrpcErrorMessage, response?: unknown) => void) => Promise<void>;
|
|
132
|
+
exchangeOauth2Code: (call: {
|
|
133
|
+
request: unknown;
|
|
134
|
+
}, callback: (error?: import("@fonoster/common").GrpcErrorMessage, response?: unknown) => void) => Promise<void>;
|
|
132
135
|
exchangeRefreshToken: (call: {
|
|
133
136
|
request: unknown;
|
|
134
137
|
}, callback: (error?: import("@fonoster/common").GrpcErrorMessage, response?: unknown) => void) => Promise<void>;
|
|
135
138
|
getPublicKey: (_: unknown, callback: (error: import("@fonoster/common").GrpcErrorMessage, response?: {
|
|
136
139
|
publicKey: string;
|
|
137
140
|
}) => void) => Promise<void>;
|
|
141
|
+
sendVerificationCode: (call: {
|
|
142
|
+
request: unknown;
|
|
143
|
+
}, callback: (error?: import("@fonoster/common").GrpcErrorMessage, response?: unknown) => void) => Promise<void>;
|
|
144
|
+
verifyCode: (call: {
|
|
145
|
+
request: unknown;
|
|
146
|
+
}, callback: (error?: import("@fonoster/common").GrpcErrorMessage, response?: unknown) => void) => Promise<void>;
|
|
138
147
|
};
|
|
139
148
|
}, {
|
|
140
149
|
definition: {
|
package/dist/envs.d.ts
CHANGED
|
@@ -10,15 +10,17 @@ export declare const CALLS_CREATE_SUBJECT = "calls.create";
|
|
|
10
10
|
export declare const CALLS_TRACK_CALL_SUBJECT = "calls.track";
|
|
11
11
|
export declare const CLOAK_ENCRYPTION_KEY: string;
|
|
12
12
|
export declare const DEFAULT_NATS_QUEUE_GROUP = "apiserver";
|
|
13
|
-
export declare const
|
|
14
|
-
export declare const FILES_SERVER_PORT: number;
|
|
13
|
+
export declare const HTTP_BRIDGE_PORT: number;
|
|
15
14
|
export declare const IDENTITY_ACCESS_TOKEN_EXPIRES_IN: string;
|
|
16
15
|
export declare const IDENTITY_AUDIENCE: string;
|
|
17
16
|
export declare const IDENTITY_ID_TOKEN_EXPIRES_IN: string;
|
|
18
17
|
export declare const IDENTITY_ISSUER: string;
|
|
18
|
+
export declare const IDENTITY_OAUTH2_GITHUB_CLIENT_ID: string;
|
|
19
|
+
export declare const IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET: string;
|
|
19
20
|
export declare const IDENTITY_PRIVATE_KEY: string;
|
|
20
21
|
export declare const IDENTITY_PUBLIC_KEY: string;
|
|
21
22
|
export declare const IDENTITY_REFRESH_TOKEN_EXPIRES_IN: string;
|
|
23
|
+
export declare const IDENTITY_WORKSPACE_INVITATION_FAIL_URL: string;
|
|
22
24
|
export declare const INFLUXDB_BUCKET: string;
|
|
23
25
|
export declare const INFLUXDB_ORG: string;
|
|
24
26
|
export declare const INFLUXDB_PASSWORD: string;
|
|
@@ -41,3 +43,7 @@ export declare const SMTP_HOST: string;
|
|
|
41
43
|
export declare const SMTP_PORT: number;
|
|
42
44
|
export declare const SMTP_SECURE: boolean;
|
|
43
45
|
export declare const SMTP_SENDER: string;
|
|
46
|
+
export declare const TEMPLATES_DIR: string;
|
|
47
|
+
export declare const TWILIO_ACCOUNT_SID: string;
|
|
48
|
+
export declare const TWILIO_AUTH_TOKEN: string;
|
|
49
|
+
export declare const TWILIO_PHONE_NUMBER: string;
|
package/dist/envs.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
var _a;
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.SMTP_SENDER = exports.SMTP_SECURE = exports.SMTP_PORT = exports.SMTP_HOST = exports.SMTP_AUTH_USER = exports.SMTP_AUTH_PASS = exports.ROUTR_DEFAULT_PEER_USERNAME = exports.ROUTR_DEFAULT_PEER_PASSWORD = exports.ROUTR_DEFAULT_PEER_NAME = exports.ROUTR_DEFAULT_PEER_AOR = exports.ROUTR_API_ENDPOINT = exports.OWNER_PASSWORD = exports.OWNER_NAME = exports.OWNER_EMAIL = exports.NATS_URL = exports.INTEGRATIONS_FILE = exports.INFLUXDB_USERNAME = exports.INFLUXDB_URL = exports.INFLUXDB_TOKEN = exports.INFLUXDB_PASSWORD = exports.INFLUXDB_ORG = exports.INFLUXDB_BUCKET = exports.IDENTITY_REFRESH_TOKEN_EXPIRES_IN = exports.IDENTITY_PUBLIC_KEY = exports.IDENTITY_PRIVATE_KEY = exports.
|
|
7
|
+
exports.TWILIO_PHONE_NUMBER = exports.TWILIO_AUTH_TOKEN = exports.TWILIO_ACCOUNT_SID = exports.TEMPLATES_DIR = exports.SMTP_SENDER = exports.SMTP_SECURE = exports.SMTP_PORT = exports.SMTP_HOST = exports.SMTP_AUTH_USER = exports.SMTP_AUTH_PASS = exports.ROUTR_DEFAULT_PEER_USERNAME = exports.ROUTR_DEFAULT_PEER_PASSWORD = exports.ROUTR_DEFAULT_PEER_NAME = exports.ROUTR_DEFAULT_PEER_AOR = exports.ROUTR_API_ENDPOINT = exports.OWNER_PASSWORD = exports.OWNER_NAME = exports.OWNER_EMAIL = exports.NATS_URL = exports.INTEGRATIONS_FILE = exports.INFLUXDB_USERNAME = exports.INFLUXDB_URL = exports.INFLUXDB_TOKEN = exports.INFLUXDB_PASSWORD = exports.INFLUXDB_ORG = exports.INFLUXDB_BUCKET = exports.IDENTITY_WORKSPACE_INVITATION_FAIL_URL = exports.IDENTITY_REFRESH_TOKEN_EXPIRES_IN = exports.IDENTITY_PUBLIC_KEY = exports.IDENTITY_PRIVATE_KEY = exports.IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET = exports.IDENTITY_OAUTH2_GITHUB_CLIENT_ID = exports.IDENTITY_ISSUER = exports.IDENTITY_ID_TOKEN_EXPIRES_IN = exports.IDENTITY_AUDIENCE = exports.IDENTITY_ACCESS_TOKEN_EXPIRES_IN = exports.HTTP_BRIDGE_PORT = exports.DEFAULT_NATS_QUEUE_GROUP = exports.CLOAK_ENCRYPTION_KEY = exports.CALLS_TRACK_CALL_SUBJECT = exports.CALLS_CREATE_SUBJECT = exports.ASTERISK_TRUNK = exports.ASTERISK_SYSTEM_DOMAIN = exports.ASTERISK_ARI_USERNAME = exports.ASTERISK_ARI_SECRET = exports.ASTERISK_ARI_PROXY_URL = exports.APP_URL = exports.APISERVER_HOST = exports.APISERVER_BIND_ADDR = void 0;
|
|
8
8
|
/*
|
|
9
9
|
* Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
|
|
10
10
|
* http://github.com/fonoster/fonoster
|
|
@@ -39,6 +39,7 @@ const e = process.env;
|
|
|
39
39
|
"SMTP_AUTH_USER",
|
|
40
40
|
"SMTP_AUTH_PASS",
|
|
41
41
|
"IDENTITY_DATABASE_URL",
|
|
42
|
+
"IDENTITY_WORKSPACE_INVITATION_FAIL_URL",
|
|
42
43
|
"DATABASE_URL",
|
|
43
44
|
"INFLUXDB_URL",
|
|
44
45
|
"INFLUXDB_INIT_USERNAME",
|
|
@@ -53,8 +54,6 @@ const e = process.env;
|
|
|
53
54
|
]);
|
|
54
55
|
const IDENTITY_PRIVATE_KEY_PATH = e.IDENTITY_PRIVATE_KEY_PATH || "/opt/fonoster/keys/private.pem";
|
|
55
56
|
const IDENTITY_PUBLIC_KEY_PATH = e.IDENTITY_PUBLIC_KEY_PATH || "/opt/fonoster/keys/public.pem";
|
|
56
|
-
(0, common_1.assertFileExists)(IDENTITY_PRIVATE_KEY_PATH);
|
|
57
|
-
(0, common_1.assertFileExists)(IDENTITY_PUBLIC_KEY_PATH);
|
|
58
57
|
exports.APISERVER_BIND_ADDR = e.APISERVER_BIND_ADDR || "0.0.0.0:50051";
|
|
59
58
|
exports.APISERVER_HOST = e.APISERVER_HOST || "apiserver";
|
|
60
59
|
// Frontend configurations
|
|
@@ -69,24 +68,31 @@ exports.CALLS_TRACK_CALL_SUBJECT = "calls.track";
|
|
|
69
68
|
// Other configurations
|
|
70
69
|
exports.CLOAK_ENCRYPTION_KEY = e.CLOAK_ENCRYPTION_KEY;
|
|
71
70
|
exports.DEFAULT_NATS_QUEUE_GROUP = "apiserver";
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
exports.FILES_SERVER_PORT = e.FILES_SERVER_PORT
|
|
75
|
-
? parseInt(e.FILES_SERVER_PORT)
|
|
71
|
+
exports.HTTP_BRIDGE_PORT = e.HTTP_BRIDGE_PORT
|
|
72
|
+
? parseInt(e.HTTP_BRIDGE_PORT)
|
|
76
73
|
: 9876;
|
|
74
|
+
// Identity configurations
|
|
77
75
|
exports.IDENTITY_ACCESS_TOKEN_EXPIRES_IN = e.IDENTITY_ACCESS_TOKEN_EXPIRES_IN || "15m";
|
|
78
76
|
exports.IDENTITY_AUDIENCE = e.IDENTITY_AUDIENCE || "api";
|
|
79
77
|
exports.IDENTITY_ID_TOKEN_EXPIRES_IN = e.IDENTITY_ID_TOKEN_EXPIRES_IN || "15m";
|
|
80
|
-
// Identity configurations
|
|
81
78
|
exports.IDENTITY_ISSUER = e.IDENTITY_ISSUER || "https://fonoster.local";
|
|
79
|
+
exports.IDENTITY_OAUTH2_GITHUB_CLIENT_ID = e.IDENTITY_OAUTH2_GITHUB_CLIENT_ID;
|
|
80
|
+
exports.IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET = e.IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET;
|
|
82
81
|
exports.IDENTITY_PRIVATE_KEY = fs_1.default.readFileSync(IDENTITY_PRIVATE_KEY_PATH, "utf8");
|
|
83
82
|
exports.IDENTITY_PUBLIC_KEY = fs_1.default.readFileSync(IDENTITY_PUBLIC_KEY_PATH, "utf8");
|
|
84
83
|
exports.IDENTITY_REFRESH_TOKEN_EXPIRES_IN = e.IDENTITY_REFRESH_TOKEN_EXPIRES_IN || "24h";
|
|
84
|
+
exports.IDENTITY_WORKSPACE_INVITATION_FAIL_URL = e.IDENTITY_WORKSPACE_INVITATION_FAIL_URL;
|
|
85
|
+
if (e.IDENTITY_OAUTH2_GITHUB_ENABLED === "true") {
|
|
86
|
+
(0, common_1.assertEnvsAreSet)([
|
|
87
|
+
"IDENTITY_OAUTH2_GITHUB_CLIENT_ID",
|
|
88
|
+
"IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET"
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
// InfluxDB configurations
|
|
85
92
|
exports.INFLUXDB_BUCKET = e.INFLUXDB_INIT_BUCKET;
|
|
86
93
|
exports.INFLUXDB_ORG = e.INFLUXDB_INIT_ORG;
|
|
87
94
|
exports.INFLUXDB_PASSWORD = e.INFLUXDB_INIT_PASSWORD;
|
|
88
95
|
exports.INFLUXDB_TOKEN = e.INFLUXDB_INIT_TOKEN;
|
|
89
|
-
// InfluxDB configurations
|
|
90
96
|
exports.INFLUXDB_URL = e.INFLUXDB_URL;
|
|
91
97
|
exports.INFLUXDB_USERNAME = e.INFLUXDB_INIT_USERNAME;
|
|
92
98
|
exports.INTEGRATIONS_FILE = e.INTEGRATIONS_FILE || "/opt/fonoster/integrations.json";
|
|
@@ -107,3 +113,9 @@ exports.SMTP_HOST = e.SMTP_HOST;
|
|
|
107
113
|
exports.SMTP_PORT = e.SMTP_PORT ? parseInt(e.SMTP_PORT) : 587;
|
|
108
114
|
exports.SMTP_SECURE = ((_a = e.SMTP_SECURE) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "true";
|
|
109
115
|
exports.SMTP_SENDER = e.SMTP_SENDER;
|
|
116
|
+
// Custom templates
|
|
117
|
+
exports.TEMPLATES_DIR = e.TEMPLATES_DIR;
|
|
118
|
+
// Twilio configurations
|
|
119
|
+
exports.TWILIO_ACCOUNT_SID = e.TWILIO_ACCOUNT_SID;
|
|
120
|
+
exports.TWILIO_AUTH_TOKEN = e.TWILIO_AUTH_TOKEN;
|
|
121
|
+
exports.TWILIO_PHONE_NUMBER = e.TWILIO_PHONE_NUMBER;
|
|
@@ -3,12 +3,12 @@ declare function createGetFnUtil(prisma: Prisma): (ref: string) => Promise<{
|
|
|
3
3
|
extended: {
|
|
4
4
|
accessKeyId: string;
|
|
5
5
|
};
|
|
6
|
-
name: string;
|
|
7
6
|
secret: string;
|
|
7
|
+
name: string;
|
|
8
8
|
ref: string;
|
|
9
|
+
accessKeyId: string;
|
|
9
10
|
createdAt: Date;
|
|
10
11
|
updatedAt: Date;
|
|
11
|
-
accessKeyId: string;
|
|
12
12
|
} & {
|
|
13
13
|
createdAt: number;
|
|
14
14
|
updatedAt: number;
|
|
@@ -36,7 +36,7 @@ function createGetFnUtil(prisma) {
|
|
|
36
36
|
where: { ref }
|
|
37
37
|
});
|
|
38
38
|
if (!response) {
|
|
39
|
-
throw (0, notFoundError_1.notFoundError)(`
|
|
39
|
+
throw (0, notFoundError_1.notFoundError)(`Resource not found: ${ref}`);
|
|
40
40
|
}
|
|
41
41
|
return (0, common_1.datesMapper)(Object.assign(Object.assign({}, response), { extended: {
|
|
42
42
|
accessKeyId: response.accessKeyId
|
|
@@ -41,5 +41,5 @@ function deleteSecret(prisma) {
|
|
|
41
41
|
yield prisma.secret.delete({ where: { ref } });
|
|
42
42
|
return { ref };
|
|
43
43
|
});
|
|
44
|
-
return (0, withErrorHandlingAndValidationAndAccess_1.withErrorHandlingAndValidationAndAccess)(fn, (ref) => getFn(ref), common_1.Validators.
|
|
44
|
+
return (0, withErrorHandlingAndValidationAndAccess_1.withErrorHandlingAndValidationAndAccess)(fn, (ref) => getFn(ref), common_1.Validators.emptySchema);
|
|
45
45
|
}
|
|
@@ -40,5 +40,5 @@ function getSecret(prisma) {
|
|
|
40
40
|
logger.verbose("call to getSecret", { ref });
|
|
41
41
|
return yield getFn(ref);
|
|
42
42
|
});
|
|
43
|
-
return (0, withErrorHandlingAndValidationAndAccess_1.withErrorHandlingAndValidationAndAccess)(fn, (ref) => getFn(ref), common_1.Validators.
|
|
43
|
+
return (0, withErrorHandlingAndValidationAndAccess_1.withErrorHandlingAndValidationAndAccess)(fn, (ref) => getFn(ref), common_1.Validators.emptySchema);
|
|
44
44
|
}
|
|
@@ -33,8 +33,10 @@ const zod_1 = require("zod");
|
|
|
33
33
|
const withErrorHandling_1 = require("./utils/withErrorHandling");
|
|
34
34
|
const requestSchema = zod_1.z.object({
|
|
35
35
|
sessionRef: zod_1.z.string(),
|
|
36
|
-
playbackRef: zod_1.z.string().optional()
|
|
37
|
-
action: zod_1.z.nativeEnum(common_1.PlaybackControlAction
|
|
36
|
+
playbackRef: zod_1.z.string().optional(),
|
|
37
|
+
action: zod_1.z.nativeEnum(common_1.PlaybackControlAction, {
|
|
38
|
+
message: "Invalid playback control action."
|
|
39
|
+
})
|
|
38
40
|
});
|
|
39
41
|
function playbackControlHandler(ari, voiceClient) {
|
|
40
42
|
return (0, withErrorHandling_1.withErrorHandling)((playbackControlReq) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -19,10 +19,10 @@ const envs_1 = require("../../envs");
|
|
|
19
19
|
const sayRequestSchema = zod_1.z.object({
|
|
20
20
|
text: zod_1.z.string(),
|
|
21
21
|
sessionRef: zod_1.z.string(),
|
|
22
|
-
playbackRef: zod_1.z.string().optional()
|
|
23
|
-
options: zod_1.z.record(zod_1.z.unknown()).optional()
|
|
22
|
+
playbackRef: zod_1.z.string().optional(),
|
|
23
|
+
options: zod_1.z.record(zod_1.z.unknown()).optional()
|
|
24
24
|
});
|
|
25
|
-
const getMediaUrl = (filename) => `sound:http://${envs_1.APISERVER_HOST}:${envs_1.
|
|
25
|
+
const getMediaUrl = (filename) => `sound:http://${envs_1.APISERVER_HOST}:${envs_1.HTTP_BRIDGE_PORT}/api/sounds/${filename}.sln16`;
|
|
26
26
|
function sayHandler(ari, voiceClient) {
|
|
27
27
|
return (0, withErrorHandling_1.withErrorHandling)((request) => __awaiter(this, void 0, void 0, function* () {
|
|
28
28
|
const { sessionRef: channelId } = request;
|
|
@@ -32,8 +32,12 @@ const common_1 = require("@fonoster/common");
|
|
|
32
32
|
const zod_1 = require("zod");
|
|
33
33
|
const withErrorHandling_1 = require("./utils/withErrorHandling");
|
|
34
34
|
const streamRequestSchema = zod_1.z.object({
|
|
35
|
-
direction: zod_1.z
|
|
36
|
-
|
|
35
|
+
direction: zod_1.z
|
|
36
|
+
.nativeEnum(common_1.StreamDirection, { message: "Invalid stream direction" })
|
|
37
|
+
.optional(),
|
|
38
|
+
format: zod_1.z
|
|
39
|
+
.nativeEnum(common_1.StreamAudioFormat, { message: "Invalid stream audio format" })
|
|
40
|
+
.optional()
|
|
37
41
|
});
|
|
38
42
|
function streamHandler(voiceClient) {
|
|
39
43
|
return (0, withErrorHandling_1.withErrorHandling)((request) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -32,7 +32,9 @@ const common_1 = require("@fonoster/common");
|
|
|
32
32
|
const zod_1 = require("zod");
|
|
33
33
|
const withErrorHandling_1 = require("./utils/withErrorHandling");
|
|
34
34
|
const gatherRequestSchema = zod_1.z.object({
|
|
35
|
-
source: zod_1.z.optional(zod_1.z.nativeEnum(common_1.StreamGatherSource
|
|
35
|
+
source: zod_1.z.optional(zod_1.z.nativeEnum(common_1.StreamGatherSource, {
|
|
36
|
+
message: "Invalid stream gather source."
|
|
37
|
+
}))
|
|
36
38
|
});
|
|
37
39
|
function streamGatherHandler(voiceClient) {
|
|
38
40
|
return (0, withErrorHandling_1.withErrorHandling)((request) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -40,14 +40,14 @@ const makeGetChannelVar_1 = require("../../utils/makeGetChannelVar");
|
|
|
40
40
|
// TODO: Needs request validation
|
|
41
41
|
function dialHandler(ari, voiceClient) {
|
|
42
42
|
return (request) => __awaiter(this, void 0, void 0, function* () {
|
|
43
|
-
const { sessionRef, destination, timeout } = request;
|
|
43
|
+
const { sessionRef: channelId, destination, timeout } = request;
|
|
44
44
|
const bridge = yield ari.bridges.create({
|
|
45
45
|
type: "mixing"
|
|
46
46
|
});
|
|
47
47
|
// eslint-disable-next-line new-cap
|
|
48
48
|
const dialed = ari.Channel();
|
|
49
|
-
yield bridge.addChannel({ channel:
|
|
50
|
-
const callerChannel = yield ari.channels.get({ channelId
|
|
49
|
+
yield bridge.addChannel({ channel: channelId });
|
|
50
|
+
const callerChannel = yield ari.channels.get({ channelId });
|
|
51
51
|
const getChannelVar = (0, makeGetChannelVar_1.makeGetChannelVar)(callerChannel);
|
|
52
52
|
const ingressNumber = (yield getChannelVar(types_1.ChannelVar.INGRESS_NUMBER))
|
|
53
53
|
.value;
|
|
@@ -62,6 +62,7 @@ function dialHandler(ari, voiceClient) {
|
|
|
62
62
|
"PJSIP_HEADER(add,X-Is-Api-Originated-Type)": "true"
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
|
+
yield ari.channels.ring({ channelId });
|
|
65
66
|
dialed.once(types_1.AriEvent.STASIS_START, (0, handleStasisStart_1.handleStasisStart)({ ari, request, bridge, dialed }));
|
|
66
67
|
dialed.once(types_1.AriEvent.CHANNEL_LEFT_BRIDGE, (0, handleChannelLeftBridge_1.handleChannelLeftBridge)({ bridge, dialed }));
|
|
67
68
|
dialed.once(types_1.AriEvent.STASIS_END, (0, handleStasisEnd_1.handleStasisEnd)(request));
|
|
@@ -36,6 +36,7 @@ function handleStasisStart(params) {
|
|
|
36
36
|
return (_, channel) => __awaiter(this, void 0, void 0, function* () {
|
|
37
37
|
try {
|
|
38
38
|
yield bridge.addChannel({ channel: dialed.id });
|
|
39
|
+
yield ari.channels.ringStop({ channelId: channel.id });
|
|
39
40
|
if ([common_1.DialRecordDirection.IN, common_1.DialRecordDirection.BOTH].includes(recordDirection)) {
|
|
40
41
|
(0, recordChannel_1.recordChannel)(ari, common_1.DialRecordDirection.IN, channel.id);
|
|
41
42
|
}
|
|
@@ -29,17 +29,28 @@ exports.gatherHandler = gatherHandler;
|
|
|
29
29
|
* limitations under the License.
|
|
30
30
|
*/
|
|
31
31
|
const common_1 = require("@fonoster/common");
|
|
32
|
+
const messages_1 = require("@fonoster/common/src/messages");
|
|
32
33
|
const zod_1 = require("zod");
|
|
33
34
|
const getTimeoutPromise_1 = require("./getTimeoutPromise");
|
|
34
35
|
const utils_1 = require("../utils");
|
|
35
36
|
const withErrorHandling_1 = require("../utils/withErrorHandling");
|
|
36
37
|
const gatherRequestSchema = zod_1.z.object({
|
|
37
|
-
source: zod_1.z
|
|
38
|
-
|
|
38
|
+
source: zod_1.z
|
|
39
|
+
.optional(zod_1.z.nativeEnum(common_1.GatherSource, { message: "Invalid gather source" }))
|
|
40
|
+
.optional(),
|
|
41
|
+
maxDigits: zod_1.z
|
|
42
|
+
.number()
|
|
43
|
+
.int({
|
|
44
|
+
message: messages_1.POSITIVE_INTEGER_MESSAGE
|
|
45
|
+
})
|
|
46
|
+
.positive({
|
|
47
|
+
message: messages_1.POSITIVE_INTEGER_MESSAGE
|
|
48
|
+
})
|
|
49
|
+
.optional(),
|
|
39
50
|
finishOnKey: zod_1.z
|
|
40
51
|
.string()
|
|
41
|
-
.regex(/^[0-9*#]$/)
|
|
42
|
-
.max(1)
|
|
52
|
+
.regex(/^[0-9*#]$/)
|
|
53
|
+
.max(1, { message: messages_1.MUST_BE_A_SINGLE_CHARACTER })
|
|
43
54
|
.optional()
|
|
44
55
|
});
|
|
45
56
|
function gatherHandler(voiceClient) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.textChunkTextByClause = textChunkTextByClause;
|
|
4
|
+
/* eslint-disable no-loops/no-loops */
|
|
5
|
+
/*
|
|
6
|
+
* Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
|
|
7
|
+
* http://github.com/fonoster/fonoster
|
|
8
|
+
*
|
|
9
|
+
* This file is part of Fonoster
|
|
10
|
+
*
|
|
11
|
+
* Licensed under the MIT License (the "License");
|
|
12
|
+
* you may not use this file except in compliance with
|
|
13
|
+
* the License. You may obtain a copy of the License at
|
|
14
|
+
*
|
|
15
|
+
* https://opensource.org/licenses/MIT
|
|
16
|
+
*
|
|
17
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
+
* See the License for the specific language governing permissions and
|
|
21
|
+
* limitations under the License.
|
|
22
|
+
*/
|
|
23
|
+
const CLAUSE_BOUNDARIES = /[.?!;]+/g;
|
|
24
|
+
function textChunkTextByClause(text) {
|
|
25
|
+
const boundaries = [...text.matchAll(CLAUSE_BOUNDARIES)];
|
|
26
|
+
const chunks = [];
|
|
27
|
+
let start = 0;
|
|
28
|
+
for (let i = 0; i < boundaries.length; i++) {
|
|
29
|
+
if (chunks.length >= 2) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
const boundary = boundaries[i];
|
|
33
|
+
const end = boundary.index + boundary[0].length;
|
|
34
|
+
chunks.push(text.slice(start, end).trim());
|
|
35
|
+
start = end;
|
|
36
|
+
}
|
|
37
|
+
const remainingText = text.slice(start).trim();
|
|
38
|
+
if (remainingText.length > 0) {
|
|
39
|
+
chunks.push(remainingText);
|
|
40
|
+
}
|
|
41
|
+
return chunks;
|
|
42
|
+
}
|
|
@@ -21,8 +21,10 @@ function withErrorHandling(fn) {
|
|
|
21
21
|
}
|
|
22
22
|
catch (err) {
|
|
23
23
|
if (err instanceof zod_1.z.ZodError) {
|
|
24
|
-
const validationError = (0, zod_validation_error_1.fromError)(err
|
|
25
|
-
|
|
24
|
+
const validationError = (0, zod_validation_error_1.fromError)(err, {
|
|
25
|
+
prefix: null
|
|
26
|
+
});
|
|
27
|
+
logger.error("Error:", {
|
|
26
28
|
message: validationError.toString()
|
|
27
29
|
});
|
|
28
30
|
}
|
|
@@ -56,7 +56,7 @@ function makeCreateContainer(prisma, pathToIntegrations) {
|
|
|
56
56
|
}
|
|
57
57
|
catch (e) {
|
|
58
58
|
// fatal error
|
|
59
|
-
const message = (0, zod_validation_error_1.fromError)(e);
|
|
59
|
+
const message = (0, zod_validation_error_1.fromError)(e, { prefix: null }).toString();
|
|
60
60
|
logger.error("integrations config is invalid", { message });
|
|
61
61
|
process.exit(1);
|
|
62
62
|
}
|
|
@@ -126,8 +126,14 @@ class Deepgram extends AbstractSpeechToText_1.AbstractSpeechToText {
|
|
|
126
126
|
}
|
|
127
127
|
static getConfigValidationSchema() {
|
|
128
128
|
return z.object({
|
|
129
|
-
languageCode: z
|
|
130
|
-
|
|
129
|
+
languageCode: z
|
|
130
|
+
.nativeEnum(common_1.VoiceLanguage, {
|
|
131
|
+
message: common_1.Messages.VALID_LANGUAGE_CODE
|
|
132
|
+
})
|
|
133
|
+
.optional(),
|
|
134
|
+
model: z
|
|
135
|
+
.nativeEnum(types_1.DeepgramModel, { message: "Invalid Deepgram model" })
|
|
136
|
+
.optional()
|
|
131
137
|
});
|
|
132
138
|
}
|
|
133
139
|
static getCredentialsValidationSchema() {
|
package/dist/voice/stt/Google.js
CHANGED
|
@@ -86,7 +86,9 @@ class Google extends AbstractSpeechToText_1.AbstractSpeechToText {
|
|
|
86
86
|
}
|
|
87
87
|
static getConfigValidationSchema() {
|
|
88
88
|
return z.object({
|
|
89
|
-
languageCode: z
|
|
89
|
+
languageCode: z
|
|
90
|
+
.nativeEnum(common_1.VoiceLanguage, { message: common_1.Messages.VALID_LANGUAGE_CODE })
|
|
91
|
+
.optional()
|
|
90
92
|
});
|
|
91
93
|
}
|
|
92
94
|
static getCredentialsValidationSchema() {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Readable } from "stream";
|
|
2
|
-
import * as sdk from "microsoft-cognitiveservices-speech-sdk";
|
|
3
2
|
import * as z from "zod";
|
|
4
3
|
import { AbstractTextToSpeech } from "./AbstractTextToSpeech";
|
|
5
4
|
import { SynthOptions } from "./types";
|
|
@@ -21,12 +20,7 @@ declare class Azure extends AbstractTextToSpeech<typeof ENGINE_NAME> {
|
|
|
21
20
|
ref: string;
|
|
22
21
|
stream: Readable;
|
|
23
22
|
}>;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
speechConfig: sdk.SpeechConfig;
|
|
27
|
-
isSSML?: boolean;
|
|
28
|
-
}): Promise<unknown>;
|
|
29
|
-
getConfigValidationSchema(): z.Schema;
|
|
30
|
-
getCredentialsValidationSchema(): z.Schema;
|
|
23
|
+
static getConfigValidationSchema(): z.Schema;
|
|
24
|
+
static getCredentialsValidationSchema(): z.Schema;
|
|
31
25
|
}
|
|
32
26
|
export { Azure, ENGINE_NAME };
|
package/dist/voice/tts/Azure.js
CHANGED
|
@@ -61,7 +61,6 @@ const isSsml_1 = require("./isSsml");
|
|
|
61
61
|
const ENGINE_NAME = "tts.azure";
|
|
62
62
|
exports.ENGINE_NAME = ENGINE_NAME;
|
|
63
63
|
const logger = (0, logger_1.getLogger)({ service: "apiserver", filePath: __filename });
|
|
64
|
-
// XXX: Must re-implement to provide an id an a Readable stream
|
|
65
64
|
class Azure extends AbstractTextToSpeech_1.AbstractTextToSpeech {
|
|
66
65
|
constructor(config) {
|
|
67
66
|
super();
|
|
@@ -78,44 +77,36 @@ class Azure extends AbstractTextToSpeech_1.AbstractTextToSpeech {
|
|
|
78
77
|
speechConfig.speechSynthesisVoiceName = options.voice;
|
|
79
78
|
speechConfig.speechSynthesisOutputFormat =
|
|
80
79
|
sdk.SpeechSynthesisOutputFormat.Riff16Khz16BitMonoPcm;
|
|
81
|
-
yield this.doSynthesize({
|
|
82
|
-
text,
|
|
83
|
-
speechConfig,
|
|
84
|
-
isSSML: (0, isSsml_1.isSsml)(text)
|
|
85
|
-
});
|
|
86
|
-
const ref = this.createMediaReference();
|
|
87
|
-
// TODO: Fix this placeholder
|
|
88
|
-
return { ref, stream: new stream_1.Readable() };
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
doSynthesize(params) {
|
|
92
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
-
const { text, speechConfig } = params;
|
|
94
80
|
const synthesizer = new sdk.SpeechSynthesizer(speechConfig);
|
|
95
|
-
|
|
96
|
-
const func =
|
|
97
|
-
|
|
98
|
-
|
|
81
|
+
const isSSML = (0, isSsml_1.isSsml)(text);
|
|
82
|
+
const func = isSSML ? "speakSsmlAsync" : "speakTextAsync";
|
|
83
|
+
const audioData = yield new Promise((resolve, reject) => {
|
|
84
|
+
const audioChunks = [];
|
|
85
|
+
synthesizer[func](text, (result) => {
|
|
99
86
|
if (result.reason === sdk.ResultReason.SynthesizingAudioCompleted) {
|
|
100
|
-
|
|
87
|
+
audioChunks.push(Buffer.from(result.audioData));
|
|
88
|
+
resolve(Buffer.concat(audioChunks));
|
|
101
89
|
}
|
|
102
90
|
else {
|
|
103
|
-
reject(new Error("
|
|
91
|
+
reject(new Error("Speech synthesis canceled: " + result.errorDetails));
|
|
104
92
|
}
|
|
105
93
|
synthesizer.close();
|
|
106
|
-
},
|
|
94
|
+
}, (err) => {
|
|
107
95
|
synthesizer.close();
|
|
108
96
|
reject(new Error(err));
|
|
109
97
|
});
|
|
110
98
|
});
|
|
99
|
+
const ref = this.createMediaReference();
|
|
100
|
+
const stream = stream_1.Readable.from(audioData);
|
|
101
|
+
return { ref, stream };
|
|
111
102
|
});
|
|
112
103
|
}
|
|
113
|
-
getConfigValidationSchema() {
|
|
104
|
+
static getConfigValidationSchema() {
|
|
114
105
|
return z.object({
|
|
115
|
-
voice: z.nativeEnum(common_1.AzureVoice)
|
|
106
|
+
voice: z.nativeEnum(common_1.AzureVoice, { message: "Invalid Azure voice" })
|
|
116
107
|
});
|
|
117
108
|
}
|
|
118
|
-
getCredentialsValidationSchema() {
|
|
109
|
+
static getCredentialsValidationSchema() {
|
|
119
110
|
return z.object({
|
|
120
111
|
subscriptionKey: z.string(),
|
|
121
112
|
serviceRegion: z.string()
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Readable } from "stream";
|
|
2
|
+
import { DeepgramClient } from "@deepgram/sdk";
|
|
2
3
|
import * as z from "zod";
|
|
3
4
|
import { AbstractTextToSpeech } from "./AbstractTextToSpeech";
|
|
4
5
|
import { SynthOptions } from "./types";
|
|
5
|
-
declare const DeepgramClient: any;
|
|
6
6
|
declare const ENGINE_NAME = "tts.deepgram";
|
|
7
7
|
type DeepgramTtsConfig = {
|
|
8
8
|
[key: string]: Record<string, string>;
|
|
@@ -11,7 +11,7 @@ type DeepgramTtsConfig = {
|
|
|
11
11
|
};
|
|
12
12
|
};
|
|
13
13
|
declare class Deepgram extends AbstractTextToSpeech<typeof ENGINE_NAME> {
|
|
14
|
-
client:
|
|
14
|
+
client: DeepgramClient;
|
|
15
15
|
engineConfig: DeepgramTtsConfig;
|
|
16
16
|
readonly engineName = "tts.deepgram";
|
|
17
17
|
protected readonly OUTPUT_FORMAT = "sln16";
|
|
@@ -23,6 +23,7 @@ declare class Deepgram extends AbstractTextToSpeech<typeof ENGINE_NAME> {
|
|
|
23
23
|
ref: string;
|
|
24
24
|
stream: Readable;
|
|
25
25
|
}>;
|
|
26
|
+
private doSynthesize;
|
|
26
27
|
static getConfigValidationSchema(): z.Schema;
|
|
27
28
|
static getCredentialsValidationSchema(): z.Schema;
|
|
28
29
|
}
|
|
@@ -33,13 +33,34 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
35
|
exports.ENGINE_NAME = exports.Deepgram = void 0;
|
|
36
|
+
/* eslint-disable no-loops/no-loops */
|
|
37
|
+
/*
|
|
38
|
+
* Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
|
|
39
|
+
* http://github.com/fonoster/fonoster
|
|
40
|
+
*
|
|
41
|
+
* This file is part of Fonoster
|
|
42
|
+
*
|
|
43
|
+
* Licensed under the MIT License (the "License");
|
|
44
|
+
* you may not use this file except in compliance with
|
|
45
|
+
* the License. You may obtain a copy of the License at
|
|
46
|
+
*
|
|
47
|
+
* https://opensource.org/licenses/MIT
|
|
48
|
+
*
|
|
49
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
50
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
51
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
52
|
+
* See the License for the specific language governing permissions and
|
|
53
|
+
* limitations under the License.
|
|
54
|
+
*/
|
|
55
|
+
const stream_1 = require("stream");
|
|
56
|
+
const sdk_1 = require("@deepgram/sdk");
|
|
36
57
|
const common_1 = require("@fonoster/common");
|
|
37
58
|
const logger_1 = require("@fonoster/logger");
|
|
38
59
|
const z = __importStar(require("zod"));
|
|
39
60
|
const AbstractTextToSpeech_1 = require("./AbstractTextToSpeech");
|
|
40
61
|
const isSsml_1 = require("./isSsml");
|
|
41
|
-
|
|
42
|
-
const
|
|
62
|
+
const streamToBuffer_1 = require("./streamToBuffer");
|
|
63
|
+
const textChunksByClause_1 = require("../handlers/utils/textChunksByClause");
|
|
43
64
|
const ENGINE_NAME = "tts.deepgram";
|
|
44
65
|
exports.ENGINE_NAME = ENGINE_NAME;
|
|
45
66
|
const logger = (0, logger_1.getLogger)({ service: "apiserver", filePath: __filename });
|
|
@@ -51,27 +72,59 @@ class Deepgram extends AbstractTextToSpeech_1.AbstractTextToSpeech {
|
|
|
51
72
|
this.CACHING_FIELDS = ["voice"];
|
|
52
73
|
this.AUDIO_ENCODING = "linear16";
|
|
53
74
|
this.SAMPLE_RATE_HERTZ = 16000;
|
|
54
|
-
this.client = createClient(config.credentials.apiKey);
|
|
75
|
+
this.client = (0, sdk_1.createClient)(config.credentials.apiKey);
|
|
55
76
|
this.engineConfig = config;
|
|
56
77
|
}
|
|
57
78
|
synthesize(text, options) {
|
|
58
79
|
return __awaiter(this, void 0, void 0, function* () {
|
|
59
80
|
logger.verbose(`synthesize [input: ${text}, isSsml=${(0, isSsml_1.isSsml)(text)} options: ${JSON.stringify(options)}]`);
|
|
60
81
|
const { voice } = this.engineConfig.config;
|
|
82
|
+
const ref = this.createMediaReference();
|
|
83
|
+
const chunks = (0, textChunksByClause_1.textChunkTextByClause)(text);
|
|
84
|
+
const stream = new stream_1.Readable({ read() { } });
|
|
85
|
+
const results = new Array(chunks.length);
|
|
86
|
+
let nextIndexToPush = 0;
|
|
87
|
+
function observeQueue() {
|
|
88
|
+
if (nextIndexToPush < results.length &&
|
|
89
|
+
results[nextIndexToPush] !== undefined) {
|
|
90
|
+
stream.push(results[nextIndexToPush]);
|
|
91
|
+
nextIndexToPush++;
|
|
92
|
+
setImmediate(observeQueue);
|
|
93
|
+
}
|
|
94
|
+
else if (nextIndexToPush < results.length) {
|
|
95
|
+
setTimeout(observeQueue, 10);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
stream.push(null);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
observeQueue();
|
|
102
|
+
chunks.forEach((text, index) => {
|
|
103
|
+
this.doSynthesize(text, voice)
|
|
104
|
+
.then((synthesizedText) => {
|
|
105
|
+
results[index] = synthesizedText;
|
|
106
|
+
})
|
|
107
|
+
.catch((error) => {
|
|
108
|
+
stream.emit("error", error);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
return { ref, stream };
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
doSynthesize(text, voice) {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
116
|
const response = yield this.client.speak.request({ text }, {
|
|
62
117
|
model: voice || common_1.DeepgramVoice.AURA_ASTERIA_EN,
|
|
63
118
|
encoding: this.AUDIO_ENCODING,
|
|
64
119
|
sample_rate: this.SAMPLE_RATE_HERTZ,
|
|
65
120
|
container: "none"
|
|
66
121
|
});
|
|
67
|
-
|
|
68
|
-
// TODO: Needs testing
|
|
69
|
-
return { ref, stream: yield response.getStream() };
|
|
122
|
+
return (yield (0, streamToBuffer_1.streamToBuffer)(yield response.getStream()));
|
|
70
123
|
});
|
|
71
124
|
}
|
|
72
125
|
static getConfigValidationSchema() {
|
|
73
126
|
return z.object({
|
|
74
|
-
voice: z.nativeEnum(common_1.DeepgramVoice)
|
|
127
|
+
voice: z.nativeEnum(common_1.DeepgramVoice, { message: "Invalid Deepgram voice" })
|
|
75
128
|
});
|
|
76
129
|
}
|
|
77
130
|
static getCredentialsValidationSchema() {
|
|
@@ -72,7 +72,9 @@ class ElevenLabs extends AbstractTextToSpeech_1.AbstractTextToSpeech {
|
|
|
72
72
|
}
|
|
73
73
|
static getConfigValidationSchema() {
|
|
74
74
|
return z.object({
|
|
75
|
-
voice: z.nativeEnum(common_1.ElevenLabsVoice
|
|
75
|
+
voice: z.nativeEnum(common_1.ElevenLabsVoice, {
|
|
76
|
+
message: "Invalid ElevenLabs voice."
|
|
77
|
+
})
|
|
76
78
|
});
|
|
77
79
|
}
|
|
78
80
|
static getCredentialsValidationSchema() {
|
package/dist/voice/tts/Google.js
CHANGED
|
@@ -88,15 +88,16 @@ class Google extends AbstractTextToSpeech_1.AbstractTextToSpeech {
|
|
|
88
88
|
name: voice
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
|
-
yield this.client.synthesizeSpeech(request);
|
|
91
|
+
const [response] = yield this.client.synthesizeSpeech(request);
|
|
92
92
|
const ref = this.createMediaReference();
|
|
93
|
-
|
|
94
|
-
return { ref, stream: new stream_1.Readable() };
|
|
93
|
+
return { ref, stream: stream_1.Readable.from(response.audioContent) };
|
|
95
94
|
});
|
|
96
95
|
}
|
|
97
96
|
static getConfigValidationSchema() {
|
|
98
97
|
return z.object({
|
|
99
|
-
voice: z
|
|
98
|
+
voice: z
|
|
99
|
+
.nativeEnum(common_1.GoogleVoice, { message: "Invalid Google voice" })
|
|
100
|
+
.optional()
|
|
100
101
|
});
|
|
101
102
|
}
|
|
102
103
|
static getCredentialsValidationSchema() {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
12
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
13
|
+
var m = o[Symbol.asyncIterator], i;
|
|
14
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
15
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
16
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.streamToBuffer = streamToBuffer;
|
|
20
|
+
/* eslint-disable no-loops/no-loops */
|
|
21
|
+
/*
|
|
22
|
+
* Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
|
|
23
|
+
* http://github.com/fonoster/fonoster
|
|
24
|
+
*
|
|
25
|
+
* This file is part of Fonoster
|
|
26
|
+
*
|
|
27
|
+
* Licensed under the MIT License (the "License");
|
|
28
|
+
* you may not use this file except in compliance with
|
|
29
|
+
* the License. You may obtain a copy of the License at
|
|
30
|
+
*
|
|
31
|
+
* https://opensource.org/licenses/MIT
|
|
32
|
+
*
|
|
33
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
34
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
35
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
36
|
+
* See the License for the specific language governing permissions and
|
|
37
|
+
* limitations under the License.
|
|
38
|
+
*/
|
|
39
|
+
function streamToBuffer(readableStream) {
|
|
40
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
var _a, readableStream_1, readableStream_1_1;
|
|
42
|
+
var _b, e_1, _c, _d;
|
|
43
|
+
const chunks = [];
|
|
44
|
+
try {
|
|
45
|
+
for (_a = true, readableStream_1 = __asyncValues(readableStream); readableStream_1_1 = yield readableStream_1.next(), _b = readableStream_1_1.done, !_b; _a = true) {
|
|
46
|
+
_d = readableStream_1_1.value;
|
|
47
|
+
_a = false;
|
|
48
|
+
const chunk = _d;
|
|
49
|
+
chunks.push(chunk);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
53
|
+
finally {
|
|
54
|
+
try {
|
|
55
|
+
if (!_a && !_b && (_c = readableStream_1.return)) yield _c.call(readableStream_1);
|
|
56
|
+
}
|
|
57
|
+
finally { if (e_1) throw e_1.error; }
|
|
58
|
+
}
|
|
59
|
+
return Buffer.concat(chunks);
|
|
60
|
+
});
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fonoster/apiserver",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.25",
|
|
4
4
|
"description": "APIServer for Fonoster",
|
|
5
5
|
"author": "Pedro Sanders <psanders@fonoster.com>",
|
|
6
6
|
"homepage": "https://github.com/fonoster/fonoster#readme",
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@deepgram/sdk": "^3.5.1",
|
|
24
|
-
"@fonoster/common": "^0.7.
|
|
25
|
-
"@fonoster/identity": "^0.7.
|
|
24
|
+
"@fonoster/common": "^0.7.25",
|
|
25
|
+
"@fonoster/identity": "^0.7.25",
|
|
26
26
|
"@fonoster/logger": "^0.7.22",
|
|
27
|
-
"@fonoster/sipnet": "^0.7.
|
|
27
|
+
"@fonoster/sipnet": "^0.7.25",
|
|
28
28
|
"@fonoster/streams": "^0.7.22",
|
|
29
|
-
"@fonoster/types": "^0.7.
|
|
29
|
+
"@fonoster/types": "^0.7.25",
|
|
30
30
|
"@google-cloud/speech": "^6.6.0",
|
|
31
31
|
"@google-cloud/text-to-speech": "^5.3.0",
|
|
32
32
|
"@grpc/grpc-js": "~1.10.6",
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"@types/uuid": "^9.0.8",
|
|
73
73
|
"@types/validator": "^13.12.0"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "f5dbfe479bd50b105ce3eb4b3c260503ef718f57"
|
|
76
76
|
}
|