@fonoster/apiserver 0.6.6 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/applications/createGetFnUtil.d.ts +1 -1
  2. package/dist/applications/getApplication.js +1 -3
  3. package/dist/applications/types.d.ts +1 -1
  4. package/dist/applications/utils/applicationWithEncodedStruct.js +20 -1
  5. package/dist/applications/utils/convertToApplicationData.js +1 -1
  6. package/dist/applications/utils/getApplicationValidationSchema.d.ts +3 -3
  7. package/dist/applications/utils/getApplicationValidationSchema.js +6 -3
  8. package/dist/applications/utils/prepareForValidation.js +1 -1
  9. package/dist/calls/ListCallsRequestSchema.d.ts +1 -4
  10. package/dist/calls/ListCallsRequestSchema.js +0 -4
  11. package/dist/calls/buildService.d.ts +2 -2
  12. package/dist/calls/buildService.js +6 -5
  13. package/dist/calls/createCall.d.ts +6 -4
  14. package/dist/calls/createCall.js +18 -8
  15. package/dist/calls/createFetchCalls.js +3 -7
  16. package/dist/calls/createFetchSingleCall.js +9 -2
  17. package/dist/calls/{trackCall.d.ts → makeTrackCall.d.ts} +3 -3
  18. package/dist/calls/makeTrackCall.js +67 -0
  19. package/dist/calls/runCallManager.js +12 -13
  20. package/dist/calls/types.d.ts +8 -42
  21. package/dist/calls/types.js +5 -38
  22. package/dist/core/seed.js +20 -0
  23. package/dist/core/services.d.ts +2 -2
  24. package/dist/envs.d.ts +0 -2
  25. package/dist/envs.js +1 -3
  26. package/dist/events/createInfluxDbPub.js +2 -1
  27. package/dist/events/mapCallDirectionToEnum.d.ts +3 -0
  28. package/dist/events/mapCallDirectionToEnum.js +37 -0
  29. package/dist/events/nats.js +5 -6
  30. package/dist/events/transformEvent.d.ts +2 -0
  31. package/dist/events/transformEvent.js +72 -0
  32. package/dist/index.js +4 -5
  33. package/dist/utils/index.d.ts +2 -0
  34. package/dist/utils/index.js +2 -0
  35. package/dist/utils/makeHandleDialEventsWithNats.d.ts +5 -0
  36. package/dist/{voice/handlers/StasisEnd.js → utils/makeHandleDialEventsWithNats.js} +10 -15
  37. package/dist/utils/makeHandleDialEventsWithVoiceClient.d.ts +5 -0
  38. package/dist/{voice/handlers/dial/handleDialEvents.js → utils/makeHandleDialEventsWithVoiceClient.js} +6 -17
  39. package/dist/utils/mapDialStatus.d.ts +3 -0
  40. package/dist/utils/mapDialStatus.js +38 -0
  41. package/dist/voice/VoiceClientImpl.d.ts +10 -1
  42. package/dist/voice/VoiceClientImpl.js +47 -7
  43. package/dist/voice/VoiceDispatcher.d.ts +6 -2
  44. package/dist/voice/VoiceDispatcher.js +50 -37
  45. package/dist/voice/connectToAri.js +3 -1
  46. package/dist/voice/createExternalMediaConfig.d.ts +3 -0
  47. package/dist/voice/createExternalMediaConfig.js +4 -1
  48. package/dist/voice/handlers/Answer.js +1 -1
  49. package/dist/voice/handlers/Hangup.js +1 -1
  50. package/dist/voice/handlers/Mute.js +1 -1
  51. package/dist/voice/handlers/Play.js +2 -2
  52. package/dist/voice/handlers/PlayDtmf.js +1 -1
  53. package/dist/voice/handlers/PlaybackControl.js +1 -1
  54. package/dist/voice/handlers/Record.js +2 -2
  55. package/dist/voice/handlers/Say.js +2 -2
  56. package/dist/voice/handlers/StreamGather.d.ts +3 -0
  57. package/dist/voice/handlers/StreamGather.js +66 -0
  58. package/dist/voice/handlers/Unmute.js +1 -1
  59. package/dist/voice/handlers/dial/Dial.js +10 -6
  60. package/dist/voice/handlers/gather/Gather.js +5 -4
  61. package/dist/voice/handlers/index.d.ts +12 -0
  62. package/dist/voice/handlers/index.js +46 -0
  63. package/dist/voice/handlers/{awaitForPlaybackFinished.js → utils/awaitForPlaybackFinished.js} +1 -1
  64. package/dist/voice/handlers/{awaitForRecordingFinished.js → utils/awaitForRecordingFinished.js} +1 -1
  65. package/dist/voice/handlers/utils/index.d.ts +3 -0
  66. package/dist/voice/handlers/utils/index.js +37 -0
  67. package/dist/voice/handlers/utils/isDtmf.d.ts +2 -0
  68. package/dist/voice/handlers/utils/isDtmf.js +24 -0
  69. package/dist/voice/integrations/findIntegrationsCredentials.d.ts +1 -1
  70. package/dist/voice/integrations/findIntegrationsCredentials.js +2 -1
  71. package/dist/voice/integrations/getSttConfig.d.ts +4 -2
  72. package/dist/voice/integrations/getSttConfig.js +4 -1
  73. package/dist/voice/integrations/getTtsConfig.d.ts +2 -1
  74. package/dist/voice/integrations/getTtsConfig.js +5 -1
  75. package/dist/voice/integrations/makeCreateContainer.js +1 -1
  76. package/dist/voice/integrations/types.d.ts +1 -1
  77. package/dist/voice/makeCreateVoiceClient.js +4 -10
  78. package/dist/voice/makeGetChannelVar.d.ts +2 -1
  79. package/dist/voice/makeGetChannelVar.js +13 -0
  80. package/dist/voice/stt/AbstractSpeechToText.d.ts +4 -3
  81. package/dist/voice/stt/Deepgram.d.ts +18 -0
  82. package/dist/voice/stt/Deepgram.js +156 -0
  83. package/dist/voice/stt/Google.d.ts +5 -6
  84. package/dist/voice/stt/Google.js +13 -13
  85. package/dist/voice/stt/SpeechToTextFactory.js +2 -0
  86. package/dist/voice/stt/types.d.ts +22 -10
  87. package/dist/voice/stt/types.js +7 -0
  88. package/dist/voice/tts/Deepgram.d.ts +25 -0
  89. package/dist/voice/tts/Deepgram.js +122 -0
  90. package/dist/voice/tts/Google.d.ts +2 -1
  91. package/dist/voice/tts/Google.js +7 -8
  92. package/dist/voice/tts/TextToSpeechFactory.js +2 -0
  93. package/dist/voice/types/ari.d.ts +2 -1
  94. package/dist/voice/types/ari.js +1 -0
  95. package/dist/voice/types/voice.d.ts +9 -1
  96. package/package.json +9 -8
  97. package/dist/calls/createTrackCallSubscriber.d.ts +0 -5
  98. package/dist/calls/createTrackCallSubscriber.js +0 -52
  99. package/dist/calls/trackCall.js +0 -63
  100. package/dist/voice/handlers/StasisEnd.d.ts +0 -4
  101. package/dist/voice/handlers/dial/handleDialEvents.d.ts +0 -5
  102. /package/dist/voice/handlers/{awaitForPlaybackFinished.d.ts → utils/awaitForPlaybackFinished.d.ts} +0 -0
  103. /package/dist/voice/handlers/{awaitForRecordingFinished.d.ts → utils/awaitForRecordingFinished.d.ts} +0 -0
  104. /package/dist/voice/handlers/{withErrorHandling.d.ts → utils/withErrorHandling.d.ts} +0 -0
  105. /package/dist/voice/handlers/{withErrorHandling.js → utils/withErrorHandling.js} +0 -0
@@ -27,7 +27,7 @@ declare function createGetFnUtil(prisma: Prisma): (ref: string) => Promise<{
27
27
  ref: string;
28
28
  createdAt: Date;
29
29
  updatedAt: Date;
30
- appEndpoint: string;
30
+ endpoint: string;
31
31
  accessKeyId: string;
32
32
  }>;
33
33
  export { createGetFnUtil };
@@ -28,7 +28,6 @@ exports.getApplication = getApplication;
28
28
  * See the License for the specific language governing permissions and
29
29
  * limitations under the License.
30
30
  */
31
- const common_1 = require("@fonoster/common");
32
31
  const identity_1 = require("@fonoster/identity");
33
32
  const logger_1 = require("@fonoster/logger");
34
33
  const createGetFnUtil_1 = require("./createGetFnUtil");
@@ -40,7 +39,6 @@ function getApplication(prisma) {
40
39
  const { ref } = call.request;
41
40
  logger.verbose("call to getApplication", { ref });
42
41
  const result = yield getFn(ref);
43
- const resultWithParsedDate = (0, common_1.datesMapper)(result);
44
- return result ? (0, applicationWithEncodedStruct_1.applicationWithEncodedStruct)(resultWithParsedDate) : null;
42
+ return result ? (0, applicationWithEncodedStruct_1.applicationWithEncodedStruct)(result) : null;
45
43
  }), (ref) => getFn(ref));
46
44
  }
@@ -3,7 +3,7 @@ type ApplicationData = {
3
3
  ref?: string;
4
4
  name: string;
5
5
  type: ApplicationType;
6
- appEndpoint: string;
6
+ endpoint: string;
7
7
  textToSpeech?: {
8
8
  create: {
9
9
  productRef: string;
@@ -1,6 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.applicationWithEncodedStruct = applicationWithEncodedStruct;
4
+ /*
5
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
6
+ * http://github.com/fonoster/fonoster
7
+ *
8
+ * This file is part of Fonoster
9
+ *
10
+ * Licensed under the MIT License (the "License");
11
+ * you may not use this file except in compliance with
12
+ * the License. You may obtain a copy of the License at
13
+ *
14
+ * https://opensource.org/licenses/MIT
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ const common_1 = require("@fonoster/common");
4
23
  const pb_util_1 = require("pb-util");
5
24
  function applicationWithEncodedStruct(application) {
6
25
  const encodeConfig = (property) => {
@@ -18,5 +37,5 @@ function applicationWithEncodedStruct(application) {
18
37
  delete intelligenceCopy.credentials;
19
38
  result.intelligence = Object.assign(Object.assign({}, intelligenceCopy), { config: encodeConfig(intelligenceCopy) });
20
39
  }
21
- return result; // Return the modified object
40
+ return (0, common_1.datesMapper)(result);
22
41
  }
@@ -9,7 +9,7 @@ function convertToApplicationData(request) {
9
9
  ref: request.ref, // Only for UpdateApplicationRequest
10
10
  name: request.name,
11
11
  type,
12
- appEndpoint: request.appEndpoint
12
+ endpoint: request.endpoint
13
13
  };
14
14
  const createProperty = (property) => {
15
15
  return property
@@ -7,7 +7,7 @@ declare function getApplicationValidationSchema(request: {
7
7
  type: z.ZodNativeEnum<{
8
8
  PROGRAMMABLE_VOICE: "PROGRAMMABLE_VOICE";
9
9
  }>;
10
- appEndpoint: z.ZodEffects<z.ZodNullable<z.ZodOptional<z.ZodString>>, string, string>;
10
+ endpoint: z.ZodEffects<z.ZodNullable<z.ZodOptional<z.ZodString>>, string, string>;
11
11
  textToSpeech: z.ZodUndefined | z.ZodObject<{
12
12
  productRef: z.ZodString;
13
13
  config: any;
@@ -35,7 +35,7 @@ declare function getApplicationValidationSchema(request: {
35
35
  }, "strip", z.ZodTypeAny, {
36
36
  name?: string;
37
37
  type?: "PROGRAMMABLE_VOICE";
38
- appEndpoint?: string;
38
+ endpoint?: string;
39
39
  textToSpeech?: {
40
40
  [x: string]: any;
41
41
  productRef?: unknown;
@@ -49,7 +49,7 @@ declare function getApplicationValidationSchema(request: {
49
49
  }, {
50
50
  name?: string;
51
51
  type?: "PROGRAMMABLE_VOICE";
52
- appEndpoint?: string;
52
+ endpoint?: string;
53
53
  textToSpeech?: {
54
54
  [x: string]: any;
55
55
  productRef?: unknown;
@@ -21,6 +21,7 @@ exports.getApplicationValidationSchema = getApplicationValidationSchema;
21
21
  */
22
22
  const client_1 = require("@prisma/client");
23
23
  const zod_1 = require("zod");
24
+ const Deepgram_1 = require("../../voice/stt/Deepgram");
24
25
  const Google_1 = require("../../voice/stt/Google");
25
26
  const Azure_1 = require("../../voice/tts/Azure");
26
27
  const Google_2 = require("../../voice/tts/Google");
@@ -35,10 +36,12 @@ const validators = {
35
36
  "tts.azure": Azure_1.Azure.getCredentialsValidationSchema
36
37
  },
37
38
  sttConfigValidators: {
38
- "stt.google": Google_1.Google.getConfigValidationSchema
39
+ "stt.google": Google_1.Google.getConfigValidationSchema,
40
+ "stt.deepgram": Deepgram_1.Deepgram.getConfigValidationSchema
39
41
  },
40
42
  sttCredentialsValidators: {
41
- "stt.google": Google_1.Google.getCredentialsValidationSchema
43
+ "stt.google": Google_1.Google.getCredentialsValidationSchema,
44
+ "stt.deepgram": Deepgram_1.Deepgram.getCredentialsValidationSchema
42
45
  }
43
46
  };
44
47
  function getApplicationValidationSchema(request) {
@@ -46,7 +49,7 @@ function getApplicationValidationSchema(request) {
46
49
  return zod_1.z.object({
47
50
  name: zod_1.z.string(),
48
51
  type: zod_1.z.nativeEnum(client_1.ApplicationType),
49
- appEndpoint: hostOrHostPortSchema_1.hostOrHostPortSchema,
52
+ endpoint: hostOrHostPortSchema_1.hostOrHostPortSchema,
50
53
  textToSpeech: ttsEngineName
51
54
  ? zod_1.z.object({
52
55
  productRef: zod_1.z.string(),
@@ -9,7 +9,7 @@ function prepareForValidation(request) {
9
9
  ref: request.ref, // Only for UpdateApplicationRequest
10
10
  name: request.name,
11
11
  type,
12
- appEndpoint: request.appEndpoint
12
+ endpoint: request.endpoint
13
13
  };
14
14
  const createProperty = (property) => {
15
15
  return property
@@ -1,11 +1,10 @@
1
1
  import { z } from "zod";
2
- import { CallStatus, CallType, HangupCause } from "./types";
2
+ import { CallStatus, CallType } from "./types";
3
3
  declare const ListCallsRequestSchema: z.ZodObject<{
4
4
  after: z.ZodNullable<z.ZodOptional<z.ZodString>>;
5
5
  before: z.ZodNullable<z.ZodOptional<z.ZodString>>;
6
6
  pageSize: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
7
7
  type: z.ZodNullable<z.ZodOptional<z.ZodNativeEnum<typeof CallType>>>;
8
- hangupCause: z.ZodNullable<z.ZodOptional<z.ZodNativeEnum<typeof HangupCause>>>;
9
8
  status: z.ZodNullable<z.ZodOptional<z.ZodNativeEnum<typeof CallStatus>>>;
10
9
  pageToken: z.ZodNullable<z.ZodOptional<z.ZodString>>;
11
10
  }, "strip", z.ZodTypeAny, {
@@ -15,7 +14,6 @@ declare const ListCallsRequestSchema: z.ZodObject<{
15
14
  pageToken?: string;
16
15
  after?: string;
17
16
  before?: string;
18
- hangupCause?: HangupCause;
19
17
  }, {
20
18
  type?: CallType;
21
19
  status?: CallStatus;
@@ -23,6 +21,5 @@ declare const ListCallsRequestSchema: z.ZodObject<{
23
21
  pageToken?: string;
24
22
  after?: string;
25
23
  before?: string;
26
- hangupCause?: HangupCause;
27
24
  }>;
28
25
  export { ListCallsRequestSchema };
@@ -42,10 +42,6 @@ const ListCallsRequestSchema = zod_1.z.object({
42
42
  })
43
43
  .optional()
44
44
  .nullable(),
45
- hangupCause: zod_1.z
46
- .nativeEnum(types_1.HangupCause, { message: "Invalid hangup cause" })
47
- .optional()
48
- .nullable(),
49
45
  status: zod_1.z
50
46
  .nativeEnum(types_1.CallStatus, { message: "Invalid call status" })
51
47
  .optional()
@@ -8,8 +8,8 @@ declare function buildService(influxdb: InfluxDBClient): Promise<{
8
8
  };
9
9
  handlers: {
10
10
  createCall: (call: {
11
- request: import("./types").CreateCallRequest;
12
- }, callback: (error?: import("@fonoster/common").GrpcErrorMessage, response?: import("./types").CreateCallResponse) => void) => Promise<void>;
11
+ request: import("@fonoster/types").CreateCallRequest;
12
+ }, callback: (error?: import("@fonoster/common").GrpcErrorMessage, response?: import("@fonoster/common").BaseApiObject) => void) => Promise<void>;
13
13
  listCalls: (call: {
14
14
  request: import("./types").ListCallsRequest;
15
15
  }, callback: (error: import("@fonoster/common").GrpcErrorMessage, response?: import("./types").ListCallsResponse) => void) => Promise<void>;
@@ -28,17 +28,18 @@ exports.buildService = buildService;
28
28
  * See the License for the specific language governing permissions and
29
29
  * limitations under the License.
30
30
  */
31
+ const nats_1 = require("nats");
31
32
  const createCall_1 = require("./createCall");
32
33
  const createCallPublisher_1 = require("./createCallPublisher");
33
- const createTrackCallSubscriber_1 = require("./createTrackCallSubscriber");
34
34
  const getCall_1 = require("./getCall");
35
35
  const listCalls_1 = require("./listCalls");
36
- const trackCall_1 = require("./trackCall");
36
+ const makeTrackCall_1 = require("./makeTrackCall");
37
+ const db_1 = require("../core/db");
37
38
  const envs_1 = require("../envs");
38
39
  function buildService(influxdb) {
39
40
  return __awaiter(this, void 0, void 0, function* () {
40
- const trackCallSubscriber = yield (0, createTrackCallSubscriber_1.createTrackCallSubscriber)(envs_1.NATS_URL);
41
41
  const callPublisher = yield (0, createCallPublisher_1.createCallPublisher)(envs_1.NATS_URL);
42
+ const nc = yield (0, nats_1.connect)({ servers: envs_1.NATS_URL });
42
43
  return {
43
44
  definition: {
44
45
  serviceName: "Calls",
@@ -47,10 +48,10 @@ function buildService(influxdb) {
47
48
  proto: "calls.proto"
48
49
  },
49
50
  handlers: {
50
- createCall: (0, createCall_1.createCall)(callPublisher),
51
+ createCall: (0, createCall_1.createCall)(db_1.prisma, callPublisher),
51
52
  listCalls: (0, listCalls_1.listCalls)(influxdb),
52
53
  getCall: (0, getCall_1.getCall)(influxdb),
53
- trackCall: (0, trackCall_1.trackCall)(trackCallSubscriber())
54
+ trackCall: (0, makeTrackCall_1.makeTrackCall)(nc)
54
55
  }
55
56
  };
56
57
  });
@@ -1,6 +1,8 @@
1
- import { GrpcErrorMessage } from "@fonoster/common";
2
- import { CallPublisher, CreateCallRequest, CreateCallResponse } from "./types";
3
- declare function createCall(publisher: CallPublisher): (call: {
1
+ import { BaseApiObject, GrpcErrorMessage } from "@fonoster/common";
2
+ import { CreateCallRequest } from "@fonoster/types";
3
+ import { CallPublisher } from "./types";
4
+ import { Prisma } from "../core/db";
5
+ declare function createCall(prisma: Prisma, publisher: CallPublisher): (call: {
4
6
  request: CreateCallRequest;
5
- }, callback: (error?: GrpcErrorMessage, response?: CreateCallResponse) => void) => Promise<void>;
7
+ }, callback: (error?: GrpcErrorMessage, response?: BaseApiObject) => void) => Promise<void>;
6
8
  export { createCall };
@@ -33,27 +33,37 @@ const identity_1 = require("@fonoster/identity");
33
33
  const logger_1 = require("@fonoster/logger");
34
34
  const uuid_1 = require("uuid");
35
35
  const zod_1 = require("zod");
36
+ const notFoundError_1 = require("../core/notFoundError");
36
37
  const logger = (0, logger_1.getLogger)({ service: "apiserver", filePath: __filename });
37
38
  const CreateCallRequestSchema = zod_1.z.object({
38
39
  from: zod_1.z.string(),
39
40
  to: zod_1.z.string(),
40
- appRef: zod_1.z.string()
41
+ appRef: zod_1.z.string(),
42
+ timeout: zod_1.z.number().optional()
41
43
  });
42
- function createCall(publisher) {
44
+ function createCall(prisma, publisher) {
43
45
  return (call, callback) => __awaiter(this, void 0, void 0, function* () {
44
46
  try {
45
- const { from, to, appRef } = call.request;
46
- CreateCallRequestSchema.parse(call.request);
47
+ const { from, to, appRef, timeout } = call.request;
47
48
  const ref = (0, uuid_1.v4)();
49
+ logger.verbose("call to createCall", Object.assign(Object.assign({}, call.request), { ref }));
50
+ CreateCallRequestSchema.parse(call.request);
48
51
  const accessKeyId = (0, identity_1.getAccessKeyIdFromCall)(call);
49
- logger.verbose("call to createCall", {
50
- accessKeyId,
52
+ const app = yield prisma.application.findUnique({
53
+ where: { ref: appRef, accessKeyId }
54
+ });
55
+ if (!app) {
56
+ throw (0, notFoundError_1.notFoundError)(`Application with ref ${appRef} not found`);
57
+ }
58
+ // TODO: Must validate that the from number exists and is owned by the user
59
+ publisher.publishCall({
51
60
  ref,
52
61
  from,
53
62
  to,
54
- appRef
63
+ appRef,
64
+ accessKeyId,
65
+ timeout: timeout || 60
55
66
  });
56
- publisher.publishCall({ ref, from, to, appRef });
57
67
  callback(null, { ref });
58
68
  }
59
69
  catch (error) {
@@ -35,7 +35,7 @@ const envs_1 = require("../envs");
35
35
  const logger = (0, logger_1.getLogger)({ service: "apiserver", filePath: __filename });
36
36
  function createFetchCalls(influxdb) {
37
37
  return (accessKeyId, request) => __awaiter(this, void 0, void 0, function* () {
38
- const { after, before, type, from, to, status, hangupCause, pageSize, pageToken } = request;
38
+ const { after, before, type, from, to, status, pageSize, pageToken } = request;
39
39
  const accessKeyIdFilter = accessKeyId
40
40
  ? (0, influxdb_client_1.flux) `and r.accessKeyId == "${accessKeyId}"`
41
41
  : (0, influxdb_client_1.flux) ``;
@@ -43,9 +43,6 @@ function createFetchCalls(influxdb) {
43
43
  const fromFilter = from ? (0, influxdb_client_1.flux) `and r.from == "${from}"` : (0, influxdb_client_1.flux) ``;
44
44
  const toFilter = to ? (0, influxdb_client_1.flux) `and r.to == "${to}"` : (0, influxdb_client_1.flux) ``;
45
45
  const statusFilter = status ? (0, influxdb_client_1.flux) `and r.status == "${status}"` : (0, influxdb_client_1.flux) ``;
46
- const hangupCauseFilter = hangupCause
47
- ? (0, influxdb_client_1.flux) `and r.hangupCause == "${hangupCause}"`
48
- : (0, influxdb_client_1.flux) ``;
49
46
  const pageTokenFilter = pageToken
50
47
  ? (0, influxdb_client_1.flux) `|> filter(fn: (r) => r.startedAtParsed < int(v: ${pageToken}))`
51
48
  : (0, influxdb_client_1.flux) ``;
@@ -56,7 +53,7 @@ function createFetchCalls(influxdb) {
56
53
  : new Date().getTime() / 1000;
57
54
  const query = (0, influxdb_client_1.flux) `from(bucket: "${envs_1.INFLUXDB_BUCKET}")
58
55
  |> range(start: ${parsedAfter})
59
- |> pivot(rowKey: ["ref"], columnKey: ["_field"], valueColumn: "_value")
56
+ |> pivot(rowKey: ["callId"], columnKey: ["_field"], valueColumn: "_value")
60
57
  |> map(fn: (r) => ({
61
58
  r with
62
59
  duration: (int(v: r.endedAt) - int(v: r.startedAt)) / 1000,
@@ -69,8 +66,7 @@ function createFetchCalls(influxdb) {
69
66
  ${typeFilter}
70
67
  ${fromFilter}
71
68
  ${toFilter}
72
- ${statusFilter}
73
- ${hangupCauseFilter})
69
+ ${statusFilter})
74
70
  |> group()
75
71
  |> sort(columns: ["startedAtParsed"], desc: true)
76
72
  ${pageTokenFilter}
@@ -28,19 +28,26 @@ exports.createFetchSingleCall = createFetchSingleCall;
28
28
  * See the License for the specific language governing permissions and
29
29
  * limitations under the License.
30
30
  */
31
+ const logger_1 = require("@fonoster/logger");
31
32
  const influxdb_client_1 = require("@influxdata/influxdb-client");
32
33
  const types_1 = require("./types");
33
34
  const envs_1 = require("../envs");
35
+ const logger = (0, logger_1.getLogger)({ service: "apiserver", filePath: __filename });
34
36
  function createFetchSingleCall(influxdb) {
35
37
  return (accessKeyId, ref) => __awaiter(this, void 0, void 0, function* () {
36
- // TODO: Look into best practices for range and limit
37
38
  const query = (0, influxdb_client_1.flux) `from(bucket: "${envs_1.INFLUXDB_BUCKET}")
38
39
  |> range(start: -360d)
39
- |> pivot(rowKey: ["_time"], columnKey: ["_field"], valueColumn: "_value")
40
+ |> pivot(rowKey: ["callId"], columnKey: ["_field"], valueColumn: "_value")
41
+ |> map(fn: (r) => ({
42
+ r with
43
+ duration: (int(v: r.endedAt) - int(v: r.startedAt)) / 1000,
44
+ startedAtParsed: int(v: r.startedAt) / 1000,
45
+ }))
40
46
  |> filter(fn: (r) => r._measurement == "${types_1.CALL_DETAIL_RECORD_MEASUREMENT}")
41
47
  |> filter(fn: (r) => r.ref == ${ref} and r.accessKeyId == "${accessKeyId}")
42
48
  |> sort(columns: ["_time"], desc: true)
43
49
  |> limit(n: 1)`;
50
+ logger.verbose("fetch single call request", { accessKeyId, ref });
44
51
  const items = (yield influxdb.collectRows(query));
45
52
  return items.length > 0 ? items[0] : null;
46
53
  });
@@ -1,5 +1,5 @@
1
+ import { NatsConnection } from "nats";
1
2
  import { z } from "zod";
2
- import { TrackCallSubscriber } from "./types";
3
3
  declare const TrackCallRequestSchema: z.ZodObject<{
4
4
  ref: z.ZodString;
5
5
  }, "strip", z.ZodTypeAny, {
@@ -8,7 +8,7 @@ declare const TrackCallRequestSchema: z.ZodObject<{
8
8
  ref?: string;
9
9
  }>;
10
10
  type TrackCallRequest = z.infer<typeof TrackCallRequestSchema>;
11
- declare function trackCall(subs: TrackCallSubscriber): (call: {
11
+ declare function makeTrackCall(nc: NatsConnection): (call: {
12
12
  request: TrackCallRequest;
13
13
  }) => void;
14
- export { trackCall };
14
+ export { makeTrackCall };
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeTrackCall = makeTrackCall;
4
+ /*
5
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
6
+ * http://github.com/fonoster/fonoster
7
+ *
8
+ * This file is part of Fonoster
9
+ *
10
+ * Licensed under the MIT License (the "License");
11
+ * you may not use this file except in compliance with
12
+ * the License. You may obtain a copy of the License at
13
+ *
14
+ * https://opensource.org/licenses/MIT
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ const common_1 = require("@fonoster/common");
23
+ const logger_1 = require("@fonoster/logger");
24
+ const zod_1 = require("zod");
25
+ const envs_1 = require("../envs");
26
+ const FINAL_STATUSES = [
27
+ common_1.DialStatus.ANSWER,
28
+ common_1.DialStatus.BUSY,
29
+ common_1.DialStatus.FAILED,
30
+ common_1.DialStatus.NOANSWER
31
+ ];
32
+ const logger = (0, logger_1.getLogger)({ service: "apiserver", filePath: __filename });
33
+ const TrackCallRequestSchema = zod_1.z.object({
34
+ ref: zod_1.z.string()
35
+ });
36
+ function makeTrackCall(nc) {
37
+ const trackingCallsMap = new Map();
38
+ const subscription = nc.subscribe(envs_1.CALLS_TRACK_CALL_SUBJECT);
39
+ subscription.callback = (err, msg) => {
40
+ // We can't do much more than log the error
41
+ if (err) {
42
+ logger.error("call to subscription.callback", { err });
43
+ return;
44
+ }
45
+ const { ref, status } = msg === null || msg === void 0 ? void 0 : msg.json();
46
+ logger.verbose("call to subscription.callback", { ref, status });
47
+ const stream = trackingCallsMap.get(ref);
48
+ if (!stream) {
49
+ // There is not request to track this call
50
+ return;
51
+ }
52
+ if (FINAL_STATUSES.includes(status)) {
53
+ stream.write({ ref, status });
54
+ setTimeout(() => stream.end(), 500);
55
+ trackingCallsMap.delete(ref);
56
+ }
57
+ else {
58
+ stream.write({ ref, status });
59
+ }
60
+ };
61
+ return (call) => {
62
+ const stream = call;
63
+ const { ref } = call.request;
64
+ logger.verbose("call to trackCall", { ref });
65
+ trackingCallsMap.set(ref, stream);
66
+ };
67
+ }
@@ -31,6 +31,7 @@ exports.createCreateCallSubscriber = createCreateCallSubscriber;
31
31
  * See the License for the specific language governing permissions and
32
32
  * limitations under the License.
33
33
  */
34
+ const common_1 = require("@fonoster/common");
34
35
  const logger_1 = require("@fonoster/logger");
35
36
  const ari_client_1 = __importDefault(require("ari-client"));
36
37
  const nats_1 = require("nats");
@@ -54,28 +55,26 @@ function createCreateCallSubscriber(config) {
54
55
  if (err) {
55
56
  logger.error(err);
56
57
  }
57
- const { ref, from, to, appRef } = msg.json();
58
- logger.info("received a new call request", Object.assign({ callRef: ref }, msg.json()));
59
- // eslint-disable-next-line new-cap
60
- const channel = ariConn.Channel();
61
- channel
62
- .originate({
63
- context: envs_1.ASTERISK_CONTEXT,
64
- extension: envs_1.ASTERISK_EXTENSION,
58
+ const { ref, from, to, appRef, accessKeyId, timeout } = msg.json();
59
+ logger.verbose("received a new call request", Object.assign({}, msg.json()));
60
+ yield ariConn.channels.originateWithId({
61
+ channelId: ref,
62
+ app: common_1.STASIS_APP_NAME,
65
63
  endpoint: `PJSIP/${envs_1.ASTERISK_TRUNK}/sip:${to}@${envs_1.ASTERISK_SYSTEM_DOMAIN}`,
64
+ timeout,
66
65
  variables: {
67
- "PJSIP_HEADER(add,X-DOD-Number)": from,
66
+ "PJSIP_HEADER(add,X-Call-Ref)": ref,
67
+ "PJSIP_HEADER(add,X-Dod-Number)": from,
68
+ "PJSIP_HEADER(add,X-Access-Key-Id)": accessKeyId,
69
+ "PJSIP_HEADER(add,X-Is-Api-Originated-Type)": "true",
68
70
  INGRESS_NUMBER: from,
69
71
  APP_REF: appRef
70
72
  }
71
- })
72
- .catch((err) => {
73
- logger.error("error creating call", { err });
74
73
  });
75
74
  });
76
75
  }
77
76
  catch (e) {
78
- logger.error("error connecting to ari", { e });
77
+ logger.error("error connecting to ari", e);
79
78
  }
80
79
  });
81
80
  }
@@ -1,44 +1,12 @@
1
- import { GrpcErrorMessage } from "@fonoster/common";
1
+ import { DialStatus, GrpcErrorMessage } from "@fonoster/common";
2
+ import { CallDirection, CallStatus, CallType } from "@fonoster/types";
2
3
  import { ParameterizedQuery } from "@influxdata/influxdb-client";
3
4
  declare const CALL_DETAIL_RECORD_MEASUREMENT = "cdr";
4
- declare enum CallType {
5
- PROGRAMMABLE = "PROGRAMMABLE",
6
- SIP_TRUNKING = "SIP_TRUNKING"
7
- }
8
- declare enum HangupCause {
9
- NORMAL_CLEARING = "NORMAL_CLEARING",
10
- CALL_REJECTED = "CALL_REJECTED",
11
- UNALLOCATED = "UNALLOCATED",
12
- NO_USER_RESPONSE = "NO_USER_RESPONSE",
13
- NO_ROUTE_DESTINATION = "NO_ROUTE_DESTINATION",
14
- NO_ANSWER = "NO_ANSWER",
15
- USER_BUSY = "USER_BUSY",
16
- NOT_ACCEPTABLE_HERE = "NOT_ACCEPTABLE_HERE",
17
- SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
18
- INVALID_NUMBER_FORMAT = "INVALID_NUMBER_FORMAT"
19
- }
20
- declare enum CallStatus {
21
- QUEUED = "QUEUED",
22
- RINGING = "RINGING",
23
- IN_PROGRESS = "IN_PROGRESS",
24
- COMPLETED = "COMPLETED",
25
- FAILED = "FAILED",
26
- BUSY = "BUSY",
27
- NO_ANSWER = "NO_ANSWER",
28
- CANCELED = "CANCELED",
29
- REJECTED = "REJECTED",
30
- TIMEOUT = "TIMEOUT",
31
- UNKNOWN = "UNKNOWN"
32
- }
33
- declare enum CallDirection {
34
- INBOUND = "INBOUND",
35
- OUTBOUND = "OUTBOUND"
36
- }
37
5
  type CallDetailRecord = {
38
6
  ref: string;
39
- type: CallType;
7
+ callId: string;
40
8
  status: CallStatus;
41
- hangupCause: HangupCause;
9
+ type: CallType;
42
10
  from: string;
43
11
  to: string;
44
12
  duration: number;
@@ -51,7 +19,6 @@ type ListCallsRequest = {
51
19
  before?: string;
52
20
  type?: CallType;
53
21
  status?: CallStatus;
54
- hangupCause?: HangupCause;
55
22
  from?: string;
56
23
  to?: string;
57
24
  pageSize?: number;
@@ -71,18 +38,17 @@ type CreateCallRequest = {
71
38
  from: string;
72
39
  to: string;
73
40
  appRef?: string;
74
- };
75
- type CreateCallResponse = {
76
- ref: string;
41
+ timeout?: number;
77
42
  };
78
43
  type CallPublisher = {
79
44
  publishCall: (event: CreateCallRequest & {
80
45
  ref: string;
46
+ accessKeyId: string;
81
47
  }) => void;
82
48
  };
83
49
  type TrackCallResponse = {
84
50
  ref: string;
85
- status: CallStatus;
51
+ status: DialStatus;
86
52
  };
87
53
  type CallStream = {
88
54
  write: (data: TrackCallResponse | GrpcErrorMessage) => void;
@@ -93,4 +59,4 @@ type TrackCallSubscriber = {
93
59
  on: (event: string, cb: (data: TrackCallResponse | Error) => void) => void;
94
60
  };
95
61
  };
96
- export { CALL_DETAIL_RECORD_MEASUREMENT, CallDetailRecord, ListCallsRequest, ListCallsResponse, GetCallRequest, CallType, CallStatus, CallDirection, InfluxDBClient, CreateCallRequest, CreateCallResponse, CallPublisher, TrackCallResponse, CallStream, TrackCallSubscriber, HangupCause };
62
+ export { CALL_DETAIL_RECORD_MEASUREMENT, CallDetailRecord, ListCallsRequest, ListCallsResponse, GetCallRequest, CallType, CallStatus, CallDirection, InfluxDBClient, CreateCallRequest, CallPublisher, TrackCallResponse, CallStream, TrackCallSubscriber };
@@ -1,42 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HangupCause = exports.CallDirection = exports.CallStatus = exports.CallType = exports.CALL_DETAIL_RECORD_MEASUREMENT = void 0;
3
+ exports.CallDirection = exports.CallStatus = exports.CallType = exports.CALL_DETAIL_RECORD_MEASUREMENT = void 0;
4
+ const types_1 = require("@fonoster/types");
5
+ Object.defineProperty(exports, "CallDirection", { enumerable: true, get: function () { return types_1.CallDirection; } });
6
+ Object.defineProperty(exports, "CallStatus", { enumerable: true, get: function () { return types_1.CallStatus; } });
7
+ Object.defineProperty(exports, "CallType", { enumerable: true, get: function () { return types_1.CallType; } });
4
8
  const CALL_DETAIL_RECORD_MEASUREMENT = "cdr";
5
9
  exports.CALL_DETAIL_RECORD_MEASUREMENT = CALL_DETAIL_RECORD_MEASUREMENT;
6
- var CallType;
7
- (function (CallType) {
8
- CallType["PROGRAMMABLE"] = "PROGRAMMABLE";
9
- CallType["SIP_TRUNKING"] = "SIP_TRUNKING";
10
- })(CallType || (exports.CallType = CallType = {}));
11
- var HangupCause;
12
- (function (HangupCause) {
13
- HangupCause["NORMAL_CLEARING"] = "NORMAL_CLEARING";
14
- HangupCause["CALL_REJECTED"] = "CALL_REJECTED";
15
- HangupCause["UNALLOCATED"] = "UNALLOCATED";
16
- HangupCause["NO_USER_RESPONSE"] = "NO_USER_RESPONSE";
17
- HangupCause["NO_ROUTE_DESTINATION"] = "NO_ROUTE_DESTINATION";
18
- HangupCause["NO_ANSWER"] = "NO_ANSWER";
19
- HangupCause["USER_BUSY"] = "USER_BUSY";
20
- HangupCause["NOT_ACCEPTABLE_HERE"] = "NOT_ACCEPTABLE_HERE";
21
- HangupCause["SERVICE_UNAVAILABLE"] = "SERVICE_UNAVAILABLE";
22
- HangupCause["INVALID_NUMBER_FORMAT"] = "INVALID_NUMBER_FORMAT";
23
- })(HangupCause || (exports.HangupCause = HangupCause = {}));
24
- var CallStatus;
25
- (function (CallStatus) {
26
- CallStatus["QUEUED"] = "QUEUED";
27
- CallStatus["RINGING"] = "RINGING";
28
- CallStatus["IN_PROGRESS"] = "IN_PROGRESS";
29
- CallStatus["COMPLETED"] = "COMPLETED";
30
- CallStatus["FAILED"] = "FAILED";
31
- CallStatus["BUSY"] = "BUSY";
32
- CallStatus["NO_ANSWER"] = "NO_ANSWER";
33
- CallStatus["CANCELED"] = "CANCELED";
34
- CallStatus["REJECTED"] = "REJECTED";
35
- CallStatus["TIMEOUT"] = "TIMEOUT";
36
- CallStatus["UNKNOWN"] = "UNKNOWN";
37
- })(CallStatus || (exports.CallStatus = CallStatus = {}));
38
- var CallDirection;
39
- (function (CallDirection) {
40
- CallDirection["INBOUND"] = "INBOUND";
41
- CallDirection["OUTBOUND"] = "OUTBOUND";
42
- })(CallDirection || (exports.CallDirection = CallDirection = {}));