@ipcom/asterisk-ari 0.0.144 → 0.0.146-beta
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 +117 -32
- package/dist/cjs/index.cjs +732 -69
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/esm/index.js +731 -69
- package/dist/esm/index.js.map +4 -4
- package/dist/types/ari-client/ariClient.d.ts +13 -1
- package/dist/types/ari-client/ariClient.d.ts.map +1 -1
- package/dist/types/ari-client/baseClient.d.ts.map +1 -1
- package/dist/types/ari-client/interfaces/events.types.d.ts +5 -0
- package/dist/types/ari-client/interfaces/events.types.d.ts.map +1 -1
- package/dist/types/ari-client/interfaces/index.d.ts +1 -1
- package/dist/types/ari-client/interfaces/index.d.ts.map +1 -1
- package/dist/types/ari-client/resources/bridges.d.ts +365 -14
- package/dist/types/ari-client/resources/bridges.d.ts.map +1 -1
- package/dist/types/ari-client/resources/channels.d.ts.map +1 -1
- package/dist/types/ari-client/resources/playbacks.d.ts.map +1 -1
- package/dist/types/ari-client/websocketClient.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/esm/index.js
CHANGED
|
@@ -611,6 +611,7 @@ var BaseClient = class {
|
|
|
611
611
|
}
|
|
612
612
|
});
|
|
613
613
|
this.addInterceptors();
|
|
614
|
+
console.log(`BaseClient initialized for ${baseUrl}`);
|
|
614
615
|
}
|
|
615
616
|
client;
|
|
616
617
|
/**
|
|
@@ -635,6 +636,7 @@ var BaseClient = class {
|
|
|
635
636
|
addInterceptors() {
|
|
636
637
|
this.client.interceptors.request.use(
|
|
637
638
|
(config) => {
|
|
639
|
+
console.log(`[Request] ${config.method?.toUpperCase()} ${config.url}`);
|
|
638
640
|
return config;
|
|
639
641
|
},
|
|
640
642
|
(error) => {
|
|
@@ -645,6 +647,7 @@ var BaseClient = class {
|
|
|
645
647
|
);
|
|
646
648
|
this.client.interceptors.response.use(
|
|
647
649
|
(response) => {
|
|
650
|
+
console.log(`[Response] ${response.status} ${response.config.url}`);
|
|
648
651
|
return response;
|
|
649
652
|
},
|
|
650
653
|
(error) => {
|
|
@@ -767,6 +770,7 @@ var BaseClient = class {
|
|
|
767
770
|
...this.client.defaults.headers.common,
|
|
768
771
|
...headers
|
|
769
772
|
};
|
|
773
|
+
console.log("Updated client headers");
|
|
770
774
|
}
|
|
771
775
|
/**
|
|
772
776
|
* Gets the current request timeout setting.
|
|
@@ -779,6 +783,7 @@ var BaseClient = class {
|
|
|
779
783
|
*/
|
|
780
784
|
setTimeout(timeout) {
|
|
781
785
|
this.client.defaults.timeout = timeout;
|
|
786
|
+
console.log(`Updated timeout to ${timeout}ms`);
|
|
782
787
|
}
|
|
783
788
|
};
|
|
784
789
|
|
|
@@ -789,7 +794,7 @@ var Applications = class {
|
|
|
789
794
|
}
|
|
790
795
|
/**
|
|
791
796
|
* Lists all applications.
|
|
792
|
-
*
|
|
797
|
+
*
|
|
793
798
|
* @returns A promise that resolves to an array of Application objects.
|
|
794
799
|
* @throws {Error} If the API response is not an array.
|
|
795
800
|
*/
|
|
@@ -802,7 +807,7 @@ var Applications = class {
|
|
|
802
807
|
}
|
|
803
808
|
/**
|
|
804
809
|
* Retrieves details of a specific application.
|
|
805
|
-
*
|
|
810
|
+
*
|
|
806
811
|
* @param appName - The name of the application to retrieve details for.
|
|
807
812
|
* @returns A promise that resolves to an ApplicationDetails object.
|
|
808
813
|
* @throws {Error} If there's an error fetching the application details.
|
|
@@ -819,7 +824,7 @@ var Applications = class {
|
|
|
819
824
|
}
|
|
820
825
|
/**
|
|
821
826
|
* Sends a message to a specific application.
|
|
822
|
-
*
|
|
827
|
+
*
|
|
823
828
|
* @param appName - The name of the application to send the message to.
|
|
824
829
|
* @param body - The message to be sent, containing an event and optional data.
|
|
825
830
|
* @returns A promise that resolves when the message is successfully sent.
|
|
@@ -912,97 +917,681 @@ var Asterisk = class {
|
|
|
912
917
|
};
|
|
913
918
|
|
|
914
919
|
// src/ari-client/resources/bridges.ts
|
|
920
|
+
import { EventEmitter } from "events";
|
|
921
|
+
import { isAxiosError as isAxiosError2 } from "axios";
|
|
922
|
+
|
|
923
|
+
// src/ari-client/interfaces/events.types.ts
|
|
924
|
+
var bridgeEvents = [
|
|
925
|
+
"BridgeCreated",
|
|
926
|
+
"BridgeDestroyed",
|
|
927
|
+
"BridgeMerged",
|
|
928
|
+
"BridgeBlindTransfer",
|
|
929
|
+
"BridgeAttendedTransfer",
|
|
930
|
+
"BridgeVideoSourceChanged"
|
|
931
|
+
];
|
|
932
|
+
|
|
933
|
+
// src/ari-client/utils.ts
|
|
934
|
+
function toQueryParams2(options) {
|
|
935
|
+
return new URLSearchParams(
|
|
936
|
+
Object.entries(options).filter(([, value]) => value !== void 0).map(([key, value]) => [key, value])
|
|
937
|
+
).toString();
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// src/ari-client/resources/bridges.ts
|
|
941
|
+
var getErrorMessage = (error) => {
|
|
942
|
+
if (isAxiosError2(error)) {
|
|
943
|
+
return error.response?.data?.message || error.message || "Um erro do axios ocorreu";
|
|
944
|
+
}
|
|
945
|
+
if (error instanceof Error) {
|
|
946
|
+
return error.message;
|
|
947
|
+
}
|
|
948
|
+
return "Um erro desconhecido ocorreu";
|
|
949
|
+
};
|
|
950
|
+
var BridgeInstance = class {
|
|
951
|
+
/**
|
|
952
|
+
* Creates a new BridgeInstance.
|
|
953
|
+
*
|
|
954
|
+
* @param client - The AriClient instance for making API calls.
|
|
955
|
+
* @param baseClient - The BaseClient instance for making HTTP requests.
|
|
956
|
+
* @param bridgeId - Optional. The ID of the bridge. If not provided, a new ID will be generated.
|
|
957
|
+
*/
|
|
958
|
+
constructor(client, baseClient, bridgeId) {
|
|
959
|
+
this.client = client;
|
|
960
|
+
this.baseClient = baseClient;
|
|
961
|
+
this.id = bridgeId || `bridge-${Date.now()}`;
|
|
962
|
+
console.log(`BridgeInstance inicializada com ID: ${this.id}`);
|
|
963
|
+
}
|
|
964
|
+
eventEmitter = new EventEmitter();
|
|
965
|
+
bridgeData = null;
|
|
966
|
+
id;
|
|
967
|
+
/**
|
|
968
|
+
* Registers a listener for specific bridge events.
|
|
969
|
+
*
|
|
970
|
+
* @param event - The type of event to listen for.
|
|
971
|
+
* @param listener - The callback function to be called when the event occurs.
|
|
972
|
+
*/
|
|
973
|
+
/**
|
|
974
|
+
* Registers a listener for specific bridge events.
|
|
975
|
+
*
|
|
976
|
+
* This method allows you to attach an event listener to the bridge instance for a specific event type.
|
|
977
|
+
* When the specified event occurs, the provided listener function will be called with the event data.
|
|
978
|
+
*
|
|
979
|
+
* @template T - The specific type of WebSocketEvent to listen for.
|
|
980
|
+
* It receives the event data as its parameter.
|
|
981
|
+
* @returns {void}
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* bridge.on('BridgeCreated', (event) => {
|
|
985
|
+
* console.log('Bridge created:', event.bridge.id);
|
|
986
|
+
* });
|
|
987
|
+
* @param event
|
|
988
|
+
* @param listener
|
|
989
|
+
*/
|
|
990
|
+
on(event, listener) {
|
|
991
|
+
if (!event) {
|
|
992
|
+
throw new Error("Event type is required");
|
|
993
|
+
}
|
|
994
|
+
const wrappedListener = (data) => {
|
|
995
|
+
if ("bridge" in data && data.bridge?.id === this.id) {
|
|
996
|
+
listener(data);
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
this.eventEmitter.on(event, wrappedListener);
|
|
1000
|
+
console.log(`Event listener registered for ${event} on bridge ${this.id}`);
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Registers a one-time listener for specific bridge events.
|
|
1004
|
+
*
|
|
1005
|
+
* @param event - The type of event to listen for.
|
|
1006
|
+
* @param listener - The callback function to be called when the event occurs.
|
|
1007
|
+
*/
|
|
1008
|
+
once(event, listener) {
|
|
1009
|
+
if (!event) {
|
|
1010
|
+
throw new Error("Event type is required");
|
|
1011
|
+
}
|
|
1012
|
+
const wrappedListener = (data) => {
|
|
1013
|
+
if ("bridge" in data && data.bridge?.id === this.id) {
|
|
1014
|
+
listener(data);
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
this.eventEmitter.once(event, wrappedListener);
|
|
1018
|
+
console.log(
|
|
1019
|
+
`One-time listener registered for ${event} on bridge ${this.id}`
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Removes event listener(s) from the bridge.
|
|
1024
|
+
*
|
|
1025
|
+
* @param event - The type of event to remove listeners for.
|
|
1026
|
+
* @param listener - Optional. The specific listener to remove. If not provided, all listeners for the event will be removed.
|
|
1027
|
+
*/
|
|
1028
|
+
off(event, listener) {
|
|
1029
|
+
if (!event) {
|
|
1030
|
+
throw new Error("Event type is required");
|
|
1031
|
+
}
|
|
1032
|
+
if (listener) {
|
|
1033
|
+
this.eventEmitter.off(event, listener);
|
|
1034
|
+
console.log(
|
|
1035
|
+
`Specific listener removed for ${event} on bridge ${this.id}`
|
|
1036
|
+
);
|
|
1037
|
+
} else {
|
|
1038
|
+
this.eventEmitter.removeAllListeners(event);
|
|
1039
|
+
console.log(`All listeners removed for ${event} on bridge ${this.id}`);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Emits an event if it corresponds to the current bridge.
|
|
1044
|
+
*
|
|
1045
|
+
* @param event - The WebSocketEvent to emit.
|
|
1046
|
+
*/
|
|
1047
|
+
emitEvent(event) {
|
|
1048
|
+
if (!event) {
|
|
1049
|
+
console.warn("Invalid event received");
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
if ("bridge" in event && event.bridge?.id === this.id) {
|
|
1053
|
+
this.eventEmitter.emit(event.type, event);
|
|
1054
|
+
console.log(`Event ${event.type} emitted for bridge ${this.id}`);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Removes all event listeners from this bridge instance.
|
|
1059
|
+
*/
|
|
1060
|
+
removeAllListeners() {
|
|
1061
|
+
this.eventEmitter.removeAllListeners();
|
|
1062
|
+
console.log(`All listeners removed from bridge ${this.id}`);
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Retrieves the current details of the bridge.
|
|
1066
|
+
*
|
|
1067
|
+
* @returns A Promise that resolves to the Bridge object containing the current details.
|
|
1068
|
+
* @throws An error if the retrieval fails.
|
|
1069
|
+
*/
|
|
1070
|
+
async get() {
|
|
1071
|
+
try {
|
|
1072
|
+
if (!this.id) {
|
|
1073
|
+
throw new Error("No bridge associated with this instance");
|
|
1074
|
+
}
|
|
1075
|
+
this.bridgeData = await this.baseClient.get(
|
|
1076
|
+
`/bridges/${this.id}`
|
|
1077
|
+
);
|
|
1078
|
+
console.log(`Details retrieved for bridge ${this.id}`);
|
|
1079
|
+
return this.bridgeData;
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
const message = getErrorMessage(error);
|
|
1082
|
+
console.error(`Error retrieving details for bridge ${this.id}:`, message);
|
|
1083
|
+
throw new Error(`Failed to get bridge details: ${message}`);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Adds channels to the bridge.
|
|
1088
|
+
*
|
|
1089
|
+
* @param request - The AddChannelRequest object containing the channels to add.
|
|
1090
|
+
* @throws An error if the operation fails.
|
|
1091
|
+
*/
|
|
1092
|
+
async add(request) {
|
|
1093
|
+
try {
|
|
1094
|
+
const queryParams = toQueryParams2({
|
|
1095
|
+
channel: Array.isArray(request.channel) ? request.channel.join(",") : request.channel,
|
|
1096
|
+
...request.role && { role: request.role }
|
|
1097
|
+
});
|
|
1098
|
+
await this.baseClient.post(
|
|
1099
|
+
`/bridges/${this.id}/addChannel?${queryParams}`
|
|
1100
|
+
);
|
|
1101
|
+
console.log(`Channels added to bridge ${this.id}`);
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
const message = getErrorMessage(error);
|
|
1104
|
+
console.error(`Error adding channels to bridge ${this.id}:`, message);
|
|
1105
|
+
throw new Error(`Failed to add channels: ${message}`);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Removes channels from the bridge.
|
|
1110
|
+
*
|
|
1111
|
+
* @param request - The RemoveChannelRequest object containing the channels to remove.
|
|
1112
|
+
* @throws An error if the operation fails.
|
|
1113
|
+
*/
|
|
1114
|
+
async remove(request) {
|
|
1115
|
+
try {
|
|
1116
|
+
const queryParams = toQueryParams2({
|
|
1117
|
+
channel: Array.isArray(request.channel) ? request.channel.join(",") : request.channel
|
|
1118
|
+
});
|
|
1119
|
+
await this.baseClient.post(
|
|
1120
|
+
`/bridges/${this.id}/removeChannel?${queryParams}`
|
|
1121
|
+
);
|
|
1122
|
+
console.log(`Channels removed from bridge ${this.id}`);
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
const message = getErrorMessage(error);
|
|
1125
|
+
console.error(`Error removing channels from bridge ${this.id}:`, message);
|
|
1126
|
+
throw new Error(`Failed to remove channels: ${message}`);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Plays media on the bridge.
|
|
1131
|
+
*
|
|
1132
|
+
* @param request - The PlayMediaRequest object containing the media details to play.
|
|
1133
|
+
* @returns A Promise that resolves to a BridgePlayback object.
|
|
1134
|
+
* @throws An error if the operation fails.
|
|
1135
|
+
*/
|
|
1136
|
+
async playMedia(request) {
|
|
1137
|
+
try {
|
|
1138
|
+
const queryParams = new URLSearchParams({
|
|
1139
|
+
...request.lang && { lang: request.lang },
|
|
1140
|
+
...request.offsetms && { offsetms: request.offsetms.toString() },
|
|
1141
|
+
...request.skipms && { skipms: request.skipms.toString() },
|
|
1142
|
+
...request.playbackId && { playbackId: request.playbackId }
|
|
1143
|
+
}).toString();
|
|
1144
|
+
const result = await this.baseClient.post(
|
|
1145
|
+
`/bridges/${this.id}/play?${queryParams}`,
|
|
1146
|
+
{ media: request.media }
|
|
1147
|
+
);
|
|
1148
|
+
console.log(`Media playback started on bridge ${this.id}`);
|
|
1149
|
+
return result;
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
const message = getErrorMessage(error);
|
|
1152
|
+
console.error(`Error playing media on bridge ${this.id}:`, message);
|
|
1153
|
+
throw new Error(`Failed to play media: ${message}`);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Stops media playback on the bridge.
|
|
1158
|
+
*
|
|
1159
|
+
* @param playbackId - The ID of the playback to stop.
|
|
1160
|
+
* @throws An error if the operation fails.
|
|
1161
|
+
*/
|
|
1162
|
+
async stopPlayback(playbackId) {
|
|
1163
|
+
try {
|
|
1164
|
+
await this.baseClient.delete(
|
|
1165
|
+
`/bridges/${this.id}/play/${playbackId}`
|
|
1166
|
+
);
|
|
1167
|
+
console.log(`Playback ${playbackId} stopped on bridge ${this.id}`);
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
const message = getErrorMessage(error);
|
|
1170
|
+
console.error(`Error stopping playback on bridge ${this.id}:`, message);
|
|
1171
|
+
throw new Error(`Failed to stop playback: ${message}`);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Sets the video source for the bridge.
|
|
1176
|
+
*
|
|
1177
|
+
* @param channelId - The ID of the channel to set as the video source.
|
|
1178
|
+
* @throws An error if the operation fails.
|
|
1179
|
+
*/
|
|
1180
|
+
async setVideoSource(channelId) {
|
|
1181
|
+
try {
|
|
1182
|
+
await this.baseClient.post(
|
|
1183
|
+
`/bridges/${this.id}/videoSource/${channelId}`
|
|
1184
|
+
);
|
|
1185
|
+
console.log(`Video source set for bridge ${this.id}`);
|
|
1186
|
+
} catch (error) {
|
|
1187
|
+
const message = getErrorMessage(error);
|
|
1188
|
+
console.error(
|
|
1189
|
+
`Error setting video source for bridge ${this.id}:`,
|
|
1190
|
+
message
|
|
1191
|
+
);
|
|
1192
|
+
throw new Error(`Failed to set video source: ${message}`);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Removes the video source from the bridge.
|
|
1197
|
+
*
|
|
1198
|
+
* @throws An error if the operation fails.
|
|
1199
|
+
*/
|
|
1200
|
+
async clearVideoSource() {
|
|
1201
|
+
try {
|
|
1202
|
+
await this.baseClient.delete(`/bridges/${this.id}/videoSource`);
|
|
1203
|
+
console.log(`Video source removed from bridge ${this.id}`);
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
const message = getErrorMessage(error);
|
|
1206
|
+
console.error(
|
|
1207
|
+
`Error removing video source from bridge ${this.id}:`,
|
|
1208
|
+
message
|
|
1209
|
+
);
|
|
1210
|
+
throw new Error(`Failed to remove video source: ${message}`);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Checks if the bridge has listeners for a specific event.
|
|
1215
|
+
*
|
|
1216
|
+
* @param event - The event type to check for listeners.
|
|
1217
|
+
* @returns A boolean indicating whether there are listeners for the event.
|
|
1218
|
+
*/
|
|
1219
|
+
hasListeners(event) {
|
|
1220
|
+
return this.eventEmitter.listenerCount(event) > 0;
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Retrieves the current bridge data without making an API call.
|
|
1224
|
+
*
|
|
1225
|
+
* @returns The current Bridge object or null if no data is available.
|
|
1226
|
+
*/
|
|
1227
|
+
getCurrentData() {
|
|
1228
|
+
return this.bridgeData;
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
915
1231
|
var Bridges = class {
|
|
916
|
-
constructor(client) {
|
|
1232
|
+
constructor(baseClient, client) {
|
|
1233
|
+
this.baseClient = baseClient;
|
|
917
1234
|
this.client = client;
|
|
918
1235
|
}
|
|
1236
|
+
bridgeInstances = /* @__PURE__ */ new Map();
|
|
1237
|
+
/**
|
|
1238
|
+
* Creates or retrieves a Bridge instance.
|
|
1239
|
+
*
|
|
1240
|
+
* This method manages the creation and retrieval of BridgeInstance objects.
|
|
1241
|
+
* If an ID is provided and an instance with that ID already exists, it returns the existing instance.
|
|
1242
|
+
* If an ID is provided but no instance exists, it creates a new instance with that ID.
|
|
1243
|
+
* If no ID is provided, it creates a new instance with a generated ID.
|
|
1244
|
+
*
|
|
1245
|
+
* @param {Object} params - The parameters for creating or retrieving a Bridge instance.
|
|
1246
|
+
* @param {string} [params.id] - Optional. The ID of the Bridge instance to create or retrieve.
|
|
1247
|
+
*
|
|
1248
|
+
* @returns {BridgeInstance} A BridgeInstance object, either newly created or retrieved from existing instances.
|
|
1249
|
+
*
|
|
1250
|
+
* @throws {Error} If there's an error in creating or retrieving the Bridge instance.
|
|
1251
|
+
*/
|
|
1252
|
+
Bridge({ id }) {
|
|
1253
|
+
try {
|
|
1254
|
+
if (!id) {
|
|
1255
|
+
const instance = new BridgeInstance(this.client, this.baseClient);
|
|
1256
|
+
this.bridgeInstances.set(instance.id, instance);
|
|
1257
|
+
console.log(`New bridge instance created with ID: ${instance.id}`);
|
|
1258
|
+
return instance;
|
|
1259
|
+
}
|
|
1260
|
+
if (!this.bridgeInstances.has(id)) {
|
|
1261
|
+
const instance = new BridgeInstance(this.client, this.baseClient, id);
|
|
1262
|
+
this.bridgeInstances.set(id, instance);
|
|
1263
|
+
console.log(`New bridge instance created with provided ID: ${id}`);
|
|
1264
|
+
return instance;
|
|
1265
|
+
}
|
|
1266
|
+
console.log(`Returning existing bridge instance: ${id}`);
|
|
1267
|
+
return this.bridgeInstances.get(id);
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
const message = getErrorMessage(error);
|
|
1270
|
+
console.error(`Error creating/retrieving bridge instance:`, message);
|
|
1271
|
+
throw new Error(`Failed to manage bridge instance: ${message}`);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Removes a bridge instance from the collection of managed bridges.
|
|
1276
|
+
*
|
|
1277
|
+
* This function removes the specified bridge instance, cleans up its event listeners,
|
|
1278
|
+
* and logs the removal. If the bridge instance doesn't exist, it logs a warning.
|
|
1279
|
+
*
|
|
1280
|
+
* @param {string} bridgeId - The unique identifier of the bridge instance to be removed.
|
|
1281
|
+
* @throws {Error} Throws an error if the bridgeId is not provided.
|
|
1282
|
+
* @returns {void}
|
|
1283
|
+
*/
|
|
1284
|
+
removeBridgeInstance(bridgeId) {
|
|
1285
|
+
if (!bridgeId) {
|
|
1286
|
+
throw new Error("ID da bridge \xE9 obrigat\xF3rio");
|
|
1287
|
+
}
|
|
1288
|
+
if (this.bridgeInstances.has(bridgeId)) {
|
|
1289
|
+
const instance = this.bridgeInstances.get(bridgeId);
|
|
1290
|
+
instance?.removeAllListeners();
|
|
1291
|
+
this.bridgeInstances.delete(bridgeId);
|
|
1292
|
+
console.log(`Inst\xE2ncia de bridge removida: ${bridgeId}`);
|
|
1293
|
+
} else {
|
|
1294
|
+
console.warn(`Tentativa de remover inst\xE2ncia inexistente: ${bridgeId}`);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
919
1297
|
/**
|
|
920
|
-
*
|
|
1298
|
+
* Propagates a WebSocket event to a specific bridge instance.
|
|
1299
|
+
*
|
|
1300
|
+
* This function checks if the received event is valid and related to a bridge,
|
|
1301
|
+
* then emits the event to the corresponding bridge instance if it exists.
|
|
1302
|
+
*
|
|
1303
|
+
* @param {WebSocketEvent} event - The WebSocket event to be propagated.
|
|
1304
|
+
* This should be an object containing information about the event,
|
|
1305
|
+
* including the bridge ID and event type.
|
|
1306
|
+
*
|
|
1307
|
+
* @returns {void}
|
|
1308
|
+
*
|
|
1309
|
+
* @remarks
|
|
1310
|
+
* - If the event is invalid (null or undefined), a warning is logged and the function returns early.
|
|
1311
|
+
* - The function checks if the event is bridge-related and if the event type is included in the predefined bridge events.
|
|
1312
|
+
* - If a matching bridge instance is found, the event is emitted to that instance.
|
|
1313
|
+
* - If no matching bridge instance is found, a warning is logged.
|
|
1314
|
+
*/
|
|
1315
|
+
propagateEventToBridge(event) {
|
|
1316
|
+
if (!event) {
|
|
1317
|
+
console.warn("Evento WebSocket inv\xE1lido recebido");
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
if ("bridge" in event && event.bridge?.id && bridgeEvents.includes(event.type)) {
|
|
1321
|
+
const instance = this.bridgeInstances.get(event.bridge.id);
|
|
1322
|
+
if (instance) {
|
|
1323
|
+
instance.emitEvent(event);
|
|
1324
|
+
console.log(
|
|
1325
|
+
`Evento propagado para bridge ${event.bridge.id}: ${event.type}`
|
|
1326
|
+
);
|
|
1327
|
+
} else {
|
|
1328
|
+
console.warn(
|
|
1329
|
+
`Nenhuma inst\xE2ncia encontrada para bridge ${event.bridge.id}`
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Lists all active bridges in the system.
|
|
1336
|
+
*
|
|
1337
|
+
* This asynchronous function retrieves a list of all currently active bridges
|
|
1338
|
+
* by making a GET request to the "/bridges" endpoint using the base client.
|
|
1339
|
+
*
|
|
1340
|
+
* @returns {Promise<Bridge[]>} A promise that resolves to an array of Bridge objects.
|
|
1341
|
+
* Each Bridge object represents an active bridge in the system.
|
|
1342
|
+
*
|
|
1343
|
+
* @throws {Error} If there's an error in fetching the bridges or if the request fails.
|
|
1344
|
+
*
|
|
1345
|
+
* @example
|
|
1346
|
+
* try {
|
|
1347
|
+
* const bridges = await bridgesInstance.list();
|
|
1348
|
+
* console.log('Active bridges:', bridges);
|
|
1349
|
+
* } catch (error) {
|
|
1350
|
+
* console.error('Failed to fetch bridges:', error);
|
|
1351
|
+
* }
|
|
921
1352
|
*/
|
|
922
1353
|
async list() {
|
|
923
|
-
return this.
|
|
1354
|
+
return this.baseClient.get("/bridges");
|
|
924
1355
|
}
|
|
925
1356
|
/**
|
|
926
|
-
* Creates a new bridge.
|
|
1357
|
+
* Creates a new bridge in the system.
|
|
1358
|
+
*
|
|
1359
|
+
* This asynchronous function sends a POST request to create a new bridge
|
|
1360
|
+
* using the provided configuration details.
|
|
1361
|
+
*
|
|
1362
|
+
* @param request - The configuration details for creating the new bridge.
|
|
1363
|
+
* @param request.type - The type of bridge to create (e.g., 'mixing', 'holding').
|
|
1364
|
+
* @param request.name - Optional. A custom name for the bridge.
|
|
1365
|
+
* @param request.bridgeId - Optional. A specific ID for the bridge. If not provided, one will be generated.
|
|
1366
|
+
*
|
|
1367
|
+
* @returns A Promise that resolves to a Bridge object representing the newly created bridge.
|
|
1368
|
+
* The Bridge object contains details such as id, technology, bridge_type, bridge_class, channels, etc.
|
|
1369
|
+
*
|
|
1370
|
+
* @throws Will throw an error if the bridge creation fails or if there's a network issue.
|
|
927
1371
|
*/
|
|
928
1372
|
async createBridge(request) {
|
|
929
|
-
return this.
|
|
1373
|
+
return this.baseClient.post("/bridges", request);
|
|
930
1374
|
}
|
|
931
1375
|
/**
|
|
932
|
-
* Retrieves
|
|
1376
|
+
* Retrieves detailed information about a specific bridge.
|
|
1377
|
+
*
|
|
1378
|
+
* This asynchronous function fetches the complete details of a bridge
|
|
1379
|
+
* identified by its unique ID. It makes a GET request to the ARI endpoint
|
|
1380
|
+
* for the specified bridge.
|
|
1381
|
+
*
|
|
1382
|
+
* @param bridgeId - The unique identifier of the bridge to retrieve details for.
|
|
1383
|
+
* This should be a string that uniquely identifies the bridge in the system.
|
|
1384
|
+
*
|
|
1385
|
+
* @returns A Promise that resolves to a Bridge object containing all the details
|
|
1386
|
+
* of the specified bridge. This includes information such as the bridge's
|
|
1387
|
+
* ID, type, channels, and other relevant properties.
|
|
1388
|
+
*
|
|
1389
|
+
* @throws Will throw an error if the bridge cannot be found, if there's a network issue,
|
|
1390
|
+
* or if the server responds with an error.
|
|
933
1391
|
*/
|
|
934
|
-
async
|
|
935
|
-
return this.
|
|
1392
|
+
async get(bridgeId) {
|
|
1393
|
+
return this.baseClient.get(`/bridges/${bridgeId}`);
|
|
936
1394
|
}
|
|
937
1395
|
/**
|
|
938
|
-
* Destroys (deletes) a specific bridge.
|
|
1396
|
+
* Destroys (deletes) a specific bridge in the system.
|
|
1397
|
+
*
|
|
1398
|
+
* This asynchronous function sends a DELETE request to remove a bridge
|
|
1399
|
+
* identified by its unique ID. Once destroyed, the bridge and all its
|
|
1400
|
+
* associated resources are permanently removed from the system.
|
|
1401
|
+
*
|
|
1402
|
+
* @param bridgeId - The unique identifier of the bridge to be destroyed.
|
|
1403
|
+
* This should be a string that uniquely identifies the bridge in the system.
|
|
1404
|
+
*
|
|
1405
|
+
* @returns A Promise that resolves to void when the bridge is successfully destroyed.
|
|
1406
|
+
* If the operation is successful, the bridge no longer exists in the system.
|
|
1407
|
+
*
|
|
1408
|
+
* @throws Will throw an error if the bridge cannot be found, if there's a network issue,
|
|
1409
|
+
* or if the server responds with an error during the deletion process.
|
|
939
1410
|
*/
|
|
940
1411
|
async destroy(bridgeId) {
|
|
941
|
-
return this.
|
|
1412
|
+
return this.baseClient.delete(`/bridges/${bridgeId}`);
|
|
942
1413
|
}
|
|
943
1414
|
/**
|
|
944
|
-
* Adds
|
|
1415
|
+
* Adds one or more channels to a specified bridge.
|
|
1416
|
+
*
|
|
1417
|
+
* This asynchronous function sends a POST request to add channels to an existing bridge.
|
|
1418
|
+
* It can handle adding a single channel or multiple channels in one operation.
|
|
1419
|
+
*
|
|
1420
|
+
* @param bridgeId - The unique identifier of the bridge to which channels will be added.
|
|
1421
|
+
* @param request - An object containing the details of the channel(s) to be added.
|
|
1422
|
+
* @param request.channel - A single channel ID or an array of channel IDs to add to the bridge.
|
|
1423
|
+
* @param request.role - Optional. Specifies the role of the channel(s) in the bridge.
|
|
1424
|
+
*
|
|
1425
|
+
* @returns A Promise that resolves to void when the operation is successful.
|
|
1426
|
+
*
|
|
1427
|
+
* @throws Will throw an error if the request fails, such as if the bridge doesn't exist
|
|
1428
|
+
* or if there's a network issue.
|
|
945
1429
|
*/
|
|
946
1430
|
async addChannels(bridgeId, request) {
|
|
947
|
-
const queryParams =
|
|
1431
|
+
const queryParams = toQueryParams2({
|
|
948
1432
|
channel: Array.isArray(request.channel) ? request.channel.join(",") : request.channel,
|
|
949
1433
|
...request.role && { role: request.role }
|
|
950
|
-
})
|
|
951
|
-
await this.
|
|
1434
|
+
});
|
|
1435
|
+
await this.baseClient.post(
|
|
952
1436
|
`/bridges/${bridgeId}/addChannel?${queryParams}`
|
|
953
1437
|
);
|
|
954
1438
|
}
|
|
955
1439
|
/**
|
|
956
|
-
* Removes
|
|
1440
|
+
* Removes one or more channels from a specified bridge.
|
|
1441
|
+
*
|
|
1442
|
+
* This asynchronous function sends a POST request to remove channels from an existing bridge.
|
|
1443
|
+
* It can handle removing a single channel or multiple channels in one operation.
|
|
1444
|
+
*
|
|
1445
|
+
* @param bridgeId - The unique identifier of the bridge from which channels will be removed.
|
|
1446
|
+
* @param request - An object containing the details of the channel(s) to be removed.
|
|
1447
|
+
* @param request.channel - A single channel ID or an array of channel IDs to remove from the bridge.
|
|
1448
|
+
*
|
|
1449
|
+
* @returns A Promise that resolves to void when the operation is successful.
|
|
1450
|
+
*
|
|
1451
|
+
* @throws Will throw an error if the request fails, such as if the bridge doesn't exist,
|
|
1452
|
+
* if the channels are not in the bridge, or if there's a network issue.
|
|
957
1453
|
*/
|
|
958
1454
|
async removeChannels(bridgeId, request) {
|
|
959
|
-
const queryParams =
|
|
1455
|
+
const queryParams = toQueryParams2({
|
|
960
1456
|
channel: Array.isArray(request.channel) ? request.channel.join(",") : request.channel
|
|
961
|
-
})
|
|
962
|
-
await this.
|
|
1457
|
+
});
|
|
1458
|
+
await this.baseClient.post(
|
|
963
1459
|
`/bridges/${bridgeId}/removeChannel?${queryParams}`
|
|
964
1460
|
);
|
|
965
1461
|
}
|
|
966
1462
|
/**
|
|
967
|
-
* Plays media
|
|
1463
|
+
* Plays media on a specified bridge.
|
|
1464
|
+
*
|
|
1465
|
+
* This asynchronous function initiates media playback on a bridge identified by its ID.
|
|
1466
|
+
* It allows for customization of the playback through various options in the request.
|
|
1467
|
+
*
|
|
1468
|
+
* @param bridgeId - The unique identifier of the bridge on which to play the media.
|
|
1469
|
+
* @param request - An object containing the media playback request details.
|
|
1470
|
+
* @param request.media - The media to be played (e.g., sound file, URL).
|
|
1471
|
+
* @param request.lang - Optional. The language of the media content.
|
|
1472
|
+
* @param request.offsetms - Optional. The offset in milliseconds to start playing from.
|
|
1473
|
+
* @param request.skipms - Optional. The number of milliseconds to skip before playing.
|
|
1474
|
+
* @param request.playbackId - Optional. A custom ID for the playback session.
|
|
1475
|
+
*
|
|
1476
|
+
* @returns A Promise that resolves to a BridgePlayback object, containing details about the initiated playback.
|
|
1477
|
+
*
|
|
1478
|
+
* @throws Will throw an error if the playback request fails or if there's a network issue.
|
|
968
1479
|
*/
|
|
969
1480
|
async playMedia(bridgeId, request) {
|
|
970
|
-
const queryParams =
|
|
1481
|
+
const queryParams = toQueryParams2({
|
|
971
1482
|
...request.lang && { lang: request.lang },
|
|
972
1483
|
...request.offsetms && { offsetms: request.offsetms.toString() },
|
|
973
1484
|
...request.skipms && { skipms: request.skipms.toString() },
|
|
974
1485
|
...request.playbackId && { playbackId: request.playbackId }
|
|
975
|
-
})
|
|
976
|
-
return this.
|
|
1486
|
+
});
|
|
1487
|
+
return this.baseClient.post(
|
|
977
1488
|
`/bridges/${bridgeId}/play?${queryParams}`,
|
|
978
1489
|
{ media: request.media }
|
|
979
1490
|
);
|
|
980
1491
|
}
|
|
981
1492
|
/**
|
|
982
|
-
* Stops media playback on a bridge.
|
|
1493
|
+
* Stops media playback on a specified bridge.
|
|
1494
|
+
*
|
|
1495
|
+
* This asynchronous function sends a DELETE request to stop the playback of media
|
|
1496
|
+
* on a bridge identified by its ID and a specific playback session.
|
|
1497
|
+
*
|
|
1498
|
+
* @param bridgeId - The unique identifier of the bridge where the playback is to be stopped.
|
|
1499
|
+
* @param playbackId - The unique identifier of the playback session to be stopped.
|
|
1500
|
+
*
|
|
1501
|
+
* @returns A Promise that resolves to void when the playback is successfully stopped.
|
|
1502
|
+
*
|
|
1503
|
+
* @throws Will throw an error if the request fails, such as if the bridge or playback session
|
|
1504
|
+
* doesn't exist, or if there's a network issue.
|
|
983
1505
|
*/
|
|
984
1506
|
async stopPlayback(bridgeId, playbackId) {
|
|
985
|
-
await this.
|
|
1507
|
+
await this.baseClient.delete(
|
|
1508
|
+
`/bridges/${bridgeId}/play/${playbackId}`
|
|
1509
|
+
);
|
|
986
1510
|
}
|
|
987
1511
|
/**
|
|
988
|
-
* Sets the video source for a bridge.
|
|
1512
|
+
* Sets the video source for a specified bridge.
|
|
1513
|
+
*
|
|
1514
|
+
* This asynchronous function configures a channel as the video source for a given bridge.
|
|
1515
|
+
* It sends a POST request to the ARI endpoint to update the bridge's video source.
|
|
1516
|
+
*
|
|
1517
|
+
* @param bridgeId - The unique identifier of the bridge for which to set the video source.
|
|
1518
|
+
* @param channelId - The unique identifier of the channel to be set as the video source.
|
|
1519
|
+
*
|
|
1520
|
+
* @returns A Promise that resolves to void when the video source is successfully set.
|
|
1521
|
+
*
|
|
1522
|
+
* @throws Will throw an error if the request fails, such as if the bridge or channel
|
|
1523
|
+
* doesn't exist, or if there's a network issue.
|
|
989
1524
|
*/
|
|
990
1525
|
async setVideoSource(bridgeId, channelId) {
|
|
991
|
-
|
|
992
|
-
|
|
1526
|
+
const queryParams = toQueryParams2({ channelId });
|
|
1527
|
+
await this.baseClient.post(
|
|
1528
|
+
`/bridges/${bridgeId}/videoSource?${queryParams}`
|
|
993
1529
|
);
|
|
994
1530
|
}
|
|
995
1531
|
/**
|
|
996
|
-
* Clears the video source for a bridge.
|
|
1532
|
+
* Clears the video source for a specified bridge.
|
|
1533
|
+
*
|
|
1534
|
+
* This asynchronous function removes the currently set video source from a bridge.
|
|
1535
|
+
* It sends a DELETE request to the ARI endpoint to clear the video source configuration.
|
|
1536
|
+
*
|
|
1537
|
+
* @param bridgeId - The unique identifier of the bridge from which to clear the video source.
|
|
1538
|
+
* This should be a string that uniquely identifies the bridge in the system.
|
|
1539
|
+
*
|
|
1540
|
+
* @returns A Promise that resolves to void when the video source is successfully cleared.
|
|
1541
|
+
* If the operation is successful, the bridge will no longer have a designated video source.
|
|
1542
|
+
*
|
|
1543
|
+
* @throws Will throw an error if the request fails, such as if the bridge doesn't exist,
|
|
1544
|
+
* if there's no video source set, or if there's a network issue.
|
|
997
1545
|
*/
|
|
998
1546
|
async clearVideoSource(bridgeId) {
|
|
999
|
-
await this.
|
|
1547
|
+
await this.baseClient.delete(`/bridges/${bridgeId}/videoSource`);
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Retrieves the count of active bridge instances.
|
|
1551
|
+
*
|
|
1552
|
+
* This function returns the total number of bridge instances currently
|
|
1553
|
+
* managed by the Bridges class. It provides a quick way to check how many
|
|
1554
|
+
* active bridges are present in the system.
|
|
1555
|
+
*
|
|
1556
|
+
* @returns {number} The count of active bridge instances.
|
|
1557
|
+
*/
|
|
1558
|
+
getInstanceCount() {
|
|
1559
|
+
return this.bridgeInstances.size;
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Checks if a bridge instance exists in the collection of managed bridges.
|
|
1563
|
+
*
|
|
1564
|
+
* This function verifies whether a bridge instance with the specified ID
|
|
1565
|
+
* is currently being managed by the Bridges class.
|
|
1566
|
+
*
|
|
1567
|
+
* @param bridgeId - The unique identifier of the bridge instance to check.
|
|
1568
|
+
* This should be a string that uniquely identifies the bridge in the system.
|
|
1569
|
+
*
|
|
1570
|
+
* @returns A boolean value indicating whether the bridge instance exists.
|
|
1571
|
+
* Returns true if the bridge instance is found, false otherwise.
|
|
1572
|
+
*/
|
|
1573
|
+
hasInstance(bridgeId) {
|
|
1574
|
+
return this.bridgeInstances.has(bridgeId);
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Retrieves all active bridge instances currently managed by the Bridges class.
|
|
1578
|
+
*
|
|
1579
|
+
* This method provides a way to access all the BridgeInstance objects that are
|
|
1580
|
+
* currently active and being managed. It returns a new Map to prevent direct
|
|
1581
|
+
* modification of the internal bridgeInstances collection.
|
|
1582
|
+
*
|
|
1583
|
+
* @returns A new Map object containing all active bridge instances, where the keys
|
|
1584
|
+
* are the bridge IDs (strings) and the values are the corresponding
|
|
1585
|
+
* BridgeInstance objects. If no bridges are active, an empty Map is returned.
|
|
1586
|
+
*/
|
|
1587
|
+
getAllInstances() {
|
|
1588
|
+
return new Map(this.bridgeInstances);
|
|
1000
1589
|
}
|
|
1001
1590
|
};
|
|
1002
1591
|
|
|
1003
1592
|
// src/ari-client/resources/channels.ts
|
|
1004
|
-
import { EventEmitter } from "events";
|
|
1005
|
-
import { isAxiosError as
|
|
1593
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
1594
|
+
import { isAxiosError as isAxiosError3 } from "axios";
|
|
1006
1595
|
|
|
1007
1596
|
// node_modules/uuid/dist/esm/stringify.js
|
|
1008
1597
|
var byteToHex = [];
|
|
@@ -1049,16 +1638,9 @@ function v4(options, buf, offset) {
|
|
|
1049
1638
|
}
|
|
1050
1639
|
var v4_default = v4;
|
|
1051
1640
|
|
|
1052
|
-
// src/ari-client/utils.ts
|
|
1053
|
-
function toQueryParams2(options) {
|
|
1054
|
-
return new URLSearchParams(
|
|
1055
|
-
Object.entries(options).filter(([, value]) => value !== void 0).map(([key, value]) => [key, value])
|
|
1056
|
-
).toString();
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
1641
|
// src/ari-client/resources/channels.ts
|
|
1060
|
-
var
|
|
1061
|
-
if (
|
|
1642
|
+
var getErrorMessage2 = (error) => {
|
|
1643
|
+
if (isAxiosError3(error)) {
|
|
1062
1644
|
return error.response?.data?.message || error.message || "An axios error occurred";
|
|
1063
1645
|
}
|
|
1064
1646
|
if (error instanceof Error) {
|
|
@@ -1071,8 +1653,9 @@ var ChannelInstance = class {
|
|
|
1071
1653
|
this.client = client;
|
|
1072
1654
|
this.baseClient = baseClient;
|
|
1073
1655
|
this.id = channelId || `channel-${Date.now()}`;
|
|
1656
|
+
console.log(`Channel instance initialized with ID: ${this.id}`);
|
|
1074
1657
|
}
|
|
1075
|
-
eventEmitter = new
|
|
1658
|
+
eventEmitter = new EventEmitter2();
|
|
1076
1659
|
channelData = null;
|
|
1077
1660
|
id;
|
|
1078
1661
|
/**
|
|
@@ -1088,6 +1671,7 @@ var ChannelInstance = class {
|
|
|
1088
1671
|
}
|
|
1089
1672
|
};
|
|
1090
1673
|
this.eventEmitter.on(event, wrappedListener);
|
|
1674
|
+
console.log(`Event listener registered for ${event} on channel ${this.id}`);
|
|
1091
1675
|
}
|
|
1092
1676
|
/**
|
|
1093
1677
|
* Registers a one-time event listener
|
|
@@ -1102,6 +1686,9 @@ var ChannelInstance = class {
|
|
|
1102
1686
|
}
|
|
1103
1687
|
};
|
|
1104
1688
|
this.eventEmitter.once(event, wrappedListener);
|
|
1689
|
+
console.log(
|
|
1690
|
+
`One-time event listener registered for ${event} on channel ${this.id}`
|
|
1691
|
+
);
|
|
1105
1692
|
}
|
|
1106
1693
|
/**
|
|
1107
1694
|
* Removes event listener(s) for a specific WebSocket event type.
|
|
@@ -1118,8 +1705,12 @@ var ChannelInstance = class {
|
|
|
1118
1705
|
}
|
|
1119
1706
|
if (listener) {
|
|
1120
1707
|
this.eventEmitter.off(event, listener);
|
|
1708
|
+
console.log(
|
|
1709
|
+
`Specific listener removed for ${event} on channel ${this.id}`
|
|
1710
|
+
);
|
|
1121
1711
|
} else {
|
|
1122
1712
|
this.eventEmitter.removeAllListeners(event);
|
|
1713
|
+
console.log(`All listeners removed for ${event} on channel ${this.id}`);
|
|
1123
1714
|
}
|
|
1124
1715
|
}
|
|
1125
1716
|
/**
|
|
@@ -1132,6 +1723,7 @@ var ChannelInstance = class {
|
|
|
1132
1723
|
}
|
|
1133
1724
|
if ("channel" in event && event.channel?.id === this.id) {
|
|
1134
1725
|
this.eventEmitter.emit(event.type, event);
|
|
1726
|
+
console.log(`Event ${event.type} emitted for channel ${this.id}`);
|
|
1135
1727
|
}
|
|
1136
1728
|
}
|
|
1137
1729
|
/**
|
|
@@ -1141,6 +1733,7 @@ var ChannelInstance = class {
|
|
|
1141
1733
|
* @return {void} This method does not return a value.
|
|
1142
1734
|
*/
|
|
1143
1735
|
removeAllListeners() {
|
|
1736
|
+
console.log(`Removendo todos os listeners para o canal ${this.id}`);
|
|
1144
1737
|
this.eventEmitter.removeAllListeners();
|
|
1145
1738
|
}
|
|
1146
1739
|
/**
|
|
@@ -1149,8 +1742,9 @@ var ChannelInstance = class {
|
|
|
1149
1742
|
async answer() {
|
|
1150
1743
|
try {
|
|
1151
1744
|
await this.baseClient.post(`/channels/${this.id}/answer`);
|
|
1745
|
+
console.log(`Channel ${this.id} answered`);
|
|
1152
1746
|
} catch (error) {
|
|
1153
|
-
const message =
|
|
1747
|
+
const message = getErrorMessage2(error);
|
|
1154
1748
|
console.error(`Error answering channel ${this.id}:`, message);
|
|
1155
1749
|
throw new Error(`Failed to answer channel: ${message}`);
|
|
1156
1750
|
}
|
|
@@ -1171,9 +1765,12 @@ var ChannelInstance = class {
|
|
|
1171
1765
|
"/channels",
|
|
1172
1766
|
data
|
|
1173
1767
|
);
|
|
1768
|
+
console.log(
|
|
1769
|
+
`Channel originated successfully with ID: ${this.channelData.id}`
|
|
1770
|
+
);
|
|
1174
1771
|
return this.channelData;
|
|
1175
1772
|
} catch (error) {
|
|
1176
|
-
const message =
|
|
1773
|
+
const message = getErrorMessage2(error);
|
|
1177
1774
|
console.error(`Error originating channel:`, message);
|
|
1178
1775
|
throw new Error(`Failed to originate channel: ${message}`);
|
|
1179
1776
|
}
|
|
@@ -1187,6 +1784,7 @@ var ChannelInstance = class {
|
|
|
1187
1784
|
}
|
|
1188
1785
|
try {
|
|
1189
1786
|
if (!this.channelData) {
|
|
1787
|
+
console.log("Initializing channel details...");
|
|
1190
1788
|
this.channelData = await this.getDetails();
|
|
1191
1789
|
}
|
|
1192
1790
|
const playback = this.client.Playback(playbackId || v4_default());
|
|
@@ -1194,9 +1792,10 @@ var ChannelInstance = class {
|
|
|
1194
1792
|
`/channels/${this.id}/play/${playback.id}`,
|
|
1195
1793
|
options
|
|
1196
1794
|
);
|
|
1795
|
+
console.log(`Media playback started on channel ${this.id}`);
|
|
1197
1796
|
return playback;
|
|
1198
1797
|
} catch (error) {
|
|
1199
|
-
const message =
|
|
1798
|
+
const message = getErrorMessage2(error);
|
|
1200
1799
|
console.error(`Error playing media on channel ${this.id}:`, message);
|
|
1201
1800
|
throw new Error(`Failed to play media: ${message}`);
|
|
1202
1801
|
}
|
|
@@ -1216,9 +1815,10 @@ var ChannelInstance = class {
|
|
|
1216
1815
|
`/channels/${this.id}`
|
|
1217
1816
|
);
|
|
1218
1817
|
this.channelData = details;
|
|
1818
|
+
console.log(`Retrieved channel details for ${this.id}`);
|
|
1219
1819
|
return details;
|
|
1220
1820
|
} catch (error) {
|
|
1221
|
-
const message =
|
|
1821
|
+
const message = getErrorMessage2(error);
|
|
1222
1822
|
console.error(
|
|
1223
1823
|
`Error retrieving channel details for ${this.id}:`,
|
|
1224
1824
|
message
|
|
@@ -1262,6 +1862,7 @@ var ChannelInstance = class {
|
|
|
1262
1862
|
*/
|
|
1263
1863
|
async hangup() {
|
|
1264
1864
|
if (!this.channelData) {
|
|
1865
|
+
console.log("Canal n\xE3o inicializado, buscando detalhes...");
|
|
1265
1866
|
this.channelData = await this.getDetails();
|
|
1266
1867
|
}
|
|
1267
1868
|
if (!this.channelData?.id) {
|
|
@@ -1450,16 +2051,19 @@ var Channels = class {
|
|
|
1450
2051
|
if (!id) {
|
|
1451
2052
|
const instance = new ChannelInstance(this.client, this.baseClient);
|
|
1452
2053
|
this.channelInstances.set(instance.id, instance);
|
|
2054
|
+
console.log(`New channel instance created with ID: ${instance.id}`);
|
|
1453
2055
|
return instance;
|
|
1454
2056
|
}
|
|
1455
2057
|
if (!this.channelInstances.has(id)) {
|
|
1456
2058
|
const instance = new ChannelInstance(this.client, this.baseClient, id);
|
|
1457
2059
|
this.channelInstances.set(id, instance);
|
|
2060
|
+
console.log(`New channel instance created with provided ID: ${id}`);
|
|
1458
2061
|
return instance;
|
|
1459
2062
|
}
|
|
2063
|
+
console.log(`Returning existing channel instance: ${id}`);
|
|
1460
2064
|
return this.channelInstances.get(id);
|
|
1461
2065
|
} catch (error) {
|
|
1462
|
-
const message =
|
|
2066
|
+
const message = getErrorMessage2(error);
|
|
1463
2067
|
console.error(`Error creating/retrieving channel instance:`, message);
|
|
1464
2068
|
throw new Error(`Failed to manage channel instance: ${message}`);
|
|
1465
2069
|
}
|
|
@@ -1476,9 +2080,11 @@ var Channels = class {
|
|
|
1476
2080
|
if (!id) {
|
|
1477
2081
|
throw new Error("No channel ID associated with this instance");
|
|
1478
2082
|
}
|
|
1479
|
-
|
|
2083
|
+
const details = await this.baseClient.get(`/channels/${id}`);
|
|
2084
|
+
console.log(`Retrieved channel details for ${id}`);
|
|
2085
|
+
return details;
|
|
1480
2086
|
} catch (error) {
|
|
1481
|
-
const message =
|
|
2087
|
+
const message = getErrorMessage2(error);
|
|
1482
2088
|
console.error(`Error retrieving channel details for ${id}:`, message);
|
|
1483
2089
|
throw new Error(`Failed to get channel details: ${message}`);
|
|
1484
2090
|
}
|
|
@@ -1494,6 +2100,7 @@ var Channels = class {
|
|
|
1494
2100
|
const instance = this.channelInstances.get(channelId);
|
|
1495
2101
|
instance?.removeAllListeners();
|
|
1496
2102
|
this.channelInstances.delete(channelId);
|
|
2103
|
+
console.log(`Channel instance removed: ${channelId}`);
|
|
1497
2104
|
} else {
|
|
1498
2105
|
console.warn(`Attempt to remove non-existent instance: ${channelId}`);
|
|
1499
2106
|
}
|
|
@@ -1510,6 +2117,9 @@ var Channels = class {
|
|
|
1510
2117
|
const instance = this.channelInstances.get(event.channel.id);
|
|
1511
2118
|
if (instance) {
|
|
1512
2119
|
instance.emitEvent(event);
|
|
2120
|
+
console.log(
|
|
2121
|
+
`Event propagated to channel ${event.channel.id}: ${event.type}`
|
|
2122
|
+
);
|
|
1513
2123
|
} else {
|
|
1514
2124
|
console.warn(`No instance found for channel ${event.channel.id}`);
|
|
1515
2125
|
}
|
|
@@ -1523,9 +2133,11 @@ var Channels = class {
|
|
|
1523
2133
|
throw new Error("Endpoint is required for channel origination");
|
|
1524
2134
|
}
|
|
1525
2135
|
try {
|
|
1526
|
-
|
|
2136
|
+
const channel = await this.baseClient.post("/channels", data);
|
|
2137
|
+
console.log(`Channel originated successfully with ID: ${channel.id}`);
|
|
2138
|
+
return channel;
|
|
1527
2139
|
} catch (error) {
|
|
1528
|
-
const message =
|
|
2140
|
+
const message = getErrorMessage2(error);
|
|
1529
2141
|
console.error(`Error originating channel:`, message);
|
|
1530
2142
|
throw new Error(`Failed to originate channel: ${message}`);
|
|
1531
2143
|
}
|
|
@@ -1539,9 +2151,10 @@ var Channels = class {
|
|
|
1539
2151
|
if (!Array.isArray(channels)) {
|
|
1540
2152
|
throw new Error("API response for /channels is not an array");
|
|
1541
2153
|
}
|
|
2154
|
+
console.log(`Retrieved ${channels.length} active channels`);
|
|
1542
2155
|
return channels;
|
|
1543
2156
|
} catch (error) {
|
|
1544
|
-
const message =
|
|
2157
|
+
const message = getErrorMessage2(error);
|
|
1545
2158
|
console.error(`Error listing channels:`, message);
|
|
1546
2159
|
throw new Error(`Failed to list channels: ${message}`);
|
|
1547
2160
|
}
|
|
@@ -1968,10 +2581,10 @@ var Endpoints = class {
|
|
|
1968
2581
|
};
|
|
1969
2582
|
|
|
1970
2583
|
// src/ari-client/resources/playbacks.ts
|
|
1971
|
-
import { EventEmitter as
|
|
1972
|
-
import { isAxiosError as
|
|
1973
|
-
var
|
|
1974
|
-
if (
|
|
2584
|
+
import { EventEmitter as EventEmitter3 } from "events";
|
|
2585
|
+
import { isAxiosError as isAxiosError4 } from "axios";
|
|
2586
|
+
var getErrorMessage3 = (error) => {
|
|
2587
|
+
if (isAxiosError4(error)) {
|
|
1975
2588
|
return error.response?.data?.message || error.message || "An axios error occurred";
|
|
1976
2589
|
}
|
|
1977
2590
|
if (error instanceof Error) {
|
|
@@ -1992,8 +2605,9 @@ var PlaybackInstance = class {
|
|
|
1992
2605
|
this.baseClient = baseClient;
|
|
1993
2606
|
this.playbackId = playbackId;
|
|
1994
2607
|
this.id = playbackId;
|
|
2608
|
+
console.log(`PlaybackInstance initialized with ID: ${this.id}`);
|
|
1995
2609
|
}
|
|
1996
|
-
eventEmitter = new
|
|
2610
|
+
eventEmitter = new EventEmitter3();
|
|
1997
2611
|
playbackData = null;
|
|
1998
2612
|
id;
|
|
1999
2613
|
/**
|
|
@@ -2012,6 +2626,9 @@ var PlaybackInstance = class {
|
|
|
2012
2626
|
}
|
|
2013
2627
|
};
|
|
2014
2628
|
this.eventEmitter.on(event, wrappedListener);
|
|
2629
|
+
console.log(
|
|
2630
|
+
`Event listener registered for ${event} on playback ${this.id}`
|
|
2631
|
+
);
|
|
2015
2632
|
}
|
|
2016
2633
|
/**
|
|
2017
2634
|
* Registers a one-time event listener for a specific WebSocket event type.
|
|
@@ -2029,6 +2646,9 @@ var PlaybackInstance = class {
|
|
|
2029
2646
|
}
|
|
2030
2647
|
};
|
|
2031
2648
|
this.eventEmitter.once(event, wrappedListener);
|
|
2649
|
+
console.log(
|
|
2650
|
+
`One-time event listener registered for ${event} on playback ${this.id}`
|
|
2651
|
+
);
|
|
2032
2652
|
}
|
|
2033
2653
|
/**
|
|
2034
2654
|
* Removes event listener(s) for a specific WebSocket event type.
|
|
@@ -2042,8 +2662,12 @@ var PlaybackInstance = class {
|
|
|
2042
2662
|
}
|
|
2043
2663
|
if (listener) {
|
|
2044
2664
|
this.eventEmitter.off(event, listener);
|
|
2665
|
+
console.log(
|
|
2666
|
+
`Specific listener removed for ${event} on playback ${this.id}`
|
|
2667
|
+
);
|
|
2045
2668
|
} else {
|
|
2046
2669
|
this.eventEmitter.removeAllListeners(event);
|
|
2670
|
+
console.log(`All listeners removed for ${event} on playback ${this.id}`);
|
|
2047
2671
|
}
|
|
2048
2672
|
}
|
|
2049
2673
|
/**
|
|
@@ -2058,6 +2682,7 @@ var PlaybackInstance = class {
|
|
|
2058
2682
|
}
|
|
2059
2683
|
if ("playback" in event && event.playback?.id === this.id) {
|
|
2060
2684
|
this.eventEmitter.emit(event.type, event);
|
|
2685
|
+
console.log(`Event ${event.type} emitted for playback ${this.id}`);
|
|
2061
2686
|
}
|
|
2062
2687
|
}
|
|
2063
2688
|
/**
|
|
@@ -2074,9 +2699,10 @@ var PlaybackInstance = class {
|
|
|
2074
2699
|
this.playbackData = await this.baseClient.get(
|
|
2075
2700
|
`/playbacks/${this.id}`
|
|
2076
2701
|
);
|
|
2702
|
+
console.log(`Retrieved playback data for ${this.id}`);
|
|
2077
2703
|
return this.playbackData;
|
|
2078
2704
|
} catch (error) {
|
|
2079
|
-
const message =
|
|
2705
|
+
const message = getErrorMessage3(error);
|
|
2080
2706
|
console.error(`Error retrieving playback data for ${this.id}:`, message);
|
|
2081
2707
|
throw new Error(`Failed to get playback data: ${message}`);
|
|
2082
2708
|
}
|
|
@@ -2095,8 +2721,11 @@ var PlaybackInstance = class {
|
|
|
2095
2721
|
await this.baseClient.post(
|
|
2096
2722
|
`/playbacks/${this.id}/control?operation=${operation}`
|
|
2097
2723
|
);
|
|
2724
|
+
console.log(
|
|
2725
|
+
`Operation ${operation} executed successfully on playback ${this.id}`
|
|
2726
|
+
);
|
|
2098
2727
|
} catch (error) {
|
|
2099
|
-
const message =
|
|
2728
|
+
const message = getErrorMessage3(error);
|
|
2100
2729
|
console.error(`Error controlling playback ${this.id}:`, message);
|
|
2101
2730
|
throw new Error(`Failed to control playback: ${message}`);
|
|
2102
2731
|
}
|
|
@@ -2112,8 +2741,9 @@ var PlaybackInstance = class {
|
|
|
2112
2741
|
}
|
|
2113
2742
|
try {
|
|
2114
2743
|
await this.baseClient.delete(`/playbacks/${this.id}`);
|
|
2744
|
+
console.log(`Playback ${this.id} stopped successfully`);
|
|
2115
2745
|
} catch (error) {
|
|
2116
|
-
const message =
|
|
2746
|
+
const message = getErrorMessage3(error);
|
|
2117
2747
|
console.error(`Error stopping playback ${this.id}:`, message);
|
|
2118
2748
|
throw new Error(`Failed to stop playback: ${message}`);
|
|
2119
2749
|
}
|
|
@@ -2123,6 +2753,7 @@ var PlaybackInstance = class {
|
|
|
2123
2753
|
*/
|
|
2124
2754
|
removeAllListeners() {
|
|
2125
2755
|
this.eventEmitter.removeAllListeners();
|
|
2756
|
+
console.log(`All listeners removed from playback ${this.id}`);
|
|
2126
2757
|
}
|
|
2127
2758
|
/**
|
|
2128
2759
|
* Checks if the playback instance has any listeners for a specific event.
|
|
@@ -2160,16 +2791,19 @@ var Playbacks = class {
|
|
|
2160
2791
|
if (!id) {
|
|
2161
2792
|
const instance = new PlaybackInstance(this.client, this.baseClient);
|
|
2162
2793
|
this.playbackInstances.set(instance.id, instance);
|
|
2794
|
+
console.log(`New playback instance created with ID: ${instance.id}`);
|
|
2163
2795
|
return instance;
|
|
2164
2796
|
}
|
|
2165
2797
|
if (!this.playbackInstances.has(id)) {
|
|
2166
2798
|
const instance = new PlaybackInstance(this.client, this.baseClient, id);
|
|
2167
2799
|
this.playbackInstances.set(id, instance);
|
|
2800
|
+
console.log(`New playback instance created with provided ID: ${id}`);
|
|
2168
2801
|
return instance;
|
|
2169
2802
|
}
|
|
2803
|
+
console.log(`Returning existing playback instance: ${id}`);
|
|
2170
2804
|
return this.playbackInstances.get(id);
|
|
2171
2805
|
} catch (error) {
|
|
2172
|
-
const message =
|
|
2806
|
+
const message = getErrorMessage3(error);
|
|
2173
2807
|
console.error(`Error creating/retrieving playback instance:`, message);
|
|
2174
2808
|
throw new Error(`Failed to manage playback instance: ${message}`);
|
|
2175
2809
|
}
|
|
@@ -2187,6 +2821,7 @@ var Playbacks = class {
|
|
|
2187
2821
|
const instance = this.playbackInstances.get(playbackId);
|
|
2188
2822
|
instance?.removeAllListeners();
|
|
2189
2823
|
this.playbackInstances.delete(playbackId);
|
|
2824
|
+
console.log(`Playback instance removed: ${playbackId}`);
|
|
2190
2825
|
} else {
|
|
2191
2826
|
console.warn(`Attempt to remove non-existent instance: ${playbackId}`);
|
|
2192
2827
|
}
|
|
@@ -2197,12 +2832,16 @@ var Playbacks = class {
|
|
|
2197
2832
|
*/
|
|
2198
2833
|
propagateEventToPlayback(event) {
|
|
2199
2834
|
if (!event) {
|
|
2835
|
+
console.warn("Invalid WebSocket event received");
|
|
2200
2836
|
return;
|
|
2201
2837
|
}
|
|
2202
2838
|
if ("playback" in event && event.playback?.id) {
|
|
2203
2839
|
const instance = this.playbackInstances.get(event.playback.id);
|
|
2204
2840
|
if (instance) {
|
|
2205
2841
|
instance.emitEvent(event);
|
|
2842
|
+
console.log(
|
|
2843
|
+
`Event propagated to playback ${event.playback.id}: ${event.type}`
|
|
2844
|
+
);
|
|
2206
2845
|
} else {
|
|
2207
2846
|
console.warn(`No instance found for playback ${event.playback.id}`);
|
|
2208
2847
|
}
|
|
@@ -2221,7 +2860,7 @@ var Playbacks = class {
|
|
|
2221
2860
|
try {
|
|
2222
2861
|
return await this.baseClient.get(`/playbacks/${playbackId}`);
|
|
2223
2862
|
} catch (error) {
|
|
2224
|
-
const message =
|
|
2863
|
+
const message = getErrorMessage3(error);
|
|
2225
2864
|
console.error(`Error getting playback details ${playbackId}:`, message);
|
|
2226
2865
|
throw new Error(`Failed to get playback details: ${message}`);
|
|
2227
2866
|
}
|
|
@@ -2239,8 +2878,9 @@ var Playbacks = class {
|
|
|
2239
2878
|
try {
|
|
2240
2879
|
const playback = this.Playback({ id: playbackId });
|
|
2241
2880
|
await playback.control(operation);
|
|
2881
|
+
console.log(`Operation ${operation} executed on playback ${playbackId}`);
|
|
2242
2882
|
} catch (error) {
|
|
2243
|
-
const message =
|
|
2883
|
+
const message = getErrorMessage3(error);
|
|
2244
2884
|
console.error(`Error controlling playback ${playbackId}:`, message);
|
|
2245
2885
|
throw new Error(`Failed to control playback: ${message}`);
|
|
2246
2886
|
}
|
|
@@ -2257,8 +2897,9 @@ var Playbacks = class {
|
|
|
2257
2897
|
try {
|
|
2258
2898
|
const playback = this.Playback({ id: playbackId });
|
|
2259
2899
|
await playback.stop();
|
|
2900
|
+
console.log(`Playback ${playbackId} stopped`);
|
|
2260
2901
|
} catch (error) {
|
|
2261
|
-
const message =
|
|
2902
|
+
const message = getErrorMessage3(error);
|
|
2262
2903
|
console.error(`Error stopping playback ${playbackId}:`, message);
|
|
2263
2904
|
throw new Error(`Failed to stop playback: ${message}`);
|
|
2264
2905
|
}
|
|
@@ -2313,12 +2954,12 @@ var Sounds = class {
|
|
|
2313
2954
|
|
|
2314
2955
|
// src/ari-client/websocketClient.ts
|
|
2315
2956
|
var import_exponential_backoff = __toESM(require_backoff(), 1);
|
|
2316
|
-
import { EventEmitter as
|
|
2957
|
+
import { EventEmitter as EventEmitter4 } from "events";
|
|
2317
2958
|
import WebSocket from "ws";
|
|
2318
2959
|
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 10;
|
|
2319
2960
|
var DEFAULT_STARTING_DELAY = 500;
|
|
2320
2961
|
var DEFAULT_MAX_DELAY = 1e4;
|
|
2321
|
-
var WebSocketClient = class extends
|
|
2962
|
+
var WebSocketClient = class extends EventEmitter4 {
|
|
2322
2963
|
/**
|
|
2323
2964
|
* Creates a new WebSocket client instance.
|
|
2324
2965
|
*
|
|
@@ -2438,7 +3079,13 @@ var WebSocketClient = class extends EventEmitter3 {
|
|
|
2438
3079
|
instancePlayback.emitEvent(event);
|
|
2439
3080
|
event.instancePlayback = instancePlayback;
|
|
2440
3081
|
}
|
|
3082
|
+
if ("bridge" in event && event.bridge?.id && this.ariClient) {
|
|
3083
|
+
const instanceBridge = this.ariClient.Bridge(event.bridge.id);
|
|
3084
|
+
instanceBridge.emitEvent(event);
|
|
3085
|
+
event.instanceBridge = instanceBridge;
|
|
3086
|
+
}
|
|
2441
3087
|
this.emit(event.type, event);
|
|
3088
|
+
console.log(`Event processed: ${event.type}`);
|
|
2442
3089
|
} catch (error) {
|
|
2443
3090
|
console.error(
|
|
2444
3091
|
"Error processing WebSocket message:",
|
|
@@ -2520,11 +3167,11 @@ var AriClient = class {
|
|
|
2520
3167
|
this.baseClient = new BaseClient(baseUrl, config.username, config.password);
|
|
2521
3168
|
this.channels = new Channels(this.baseClient, this);
|
|
2522
3169
|
this.playbacks = new Playbacks(this.baseClient, this);
|
|
3170
|
+
this.bridges = new Bridges(this.baseClient, this);
|
|
2523
3171
|
this.endpoints = new Endpoints(this.baseClient);
|
|
2524
3172
|
this.applications = new Applications(this.baseClient);
|
|
2525
3173
|
this.sounds = new Sounds(this.baseClient);
|
|
2526
3174
|
this.asterisk = new Asterisk(this.baseClient);
|
|
2527
|
-
this.bridges = new Bridges(this.baseClient);
|
|
2528
3175
|
console.log(`ARI Client initialized with base URL: ${baseUrl}`);
|
|
2529
3176
|
}
|
|
2530
3177
|
baseClient;
|
|
@@ -2640,6 +3287,20 @@ var AriClient = class {
|
|
|
2640
3287
|
Playback(playbackId, _app) {
|
|
2641
3288
|
return this.playbacks.Playback({ id: playbackId });
|
|
2642
3289
|
}
|
|
3290
|
+
/**
|
|
3291
|
+
* Creates or retrieves a Bridge instance.
|
|
3292
|
+
*
|
|
3293
|
+
* This function allows you to create a new Bridge instance or retrieve an existing one
|
|
3294
|
+
* based on the provided bridge ID.
|
|
3295
|
+
*
|
|
3296
|
+
* @param {string} [bridgeId] - Optional ID of an existing bridge. If provided, retrieves the
|
|
3297
|
+
* existing bridge with this ID. If omitted, creates a new bridge.
|
|
3298
|
+
* @returns {BridgeInstance} A new or existing Bridge instance that can be used to interact
|
|
3299
|
+
* with the Asterisk bridge.
|
|
3300
|
+
*/
|
|
3301
|
+
Bridge(bridgeId) {
|
|
3302
|
+
return this.bridges.Bridge({ id: bridgeId });
|
|
3303
|
+
}
|
|
2643
3304
|
/**
|
|
2644
3305
|
* Gets the current WebSocket connection status.
|
|
2645
3306
|
*
|
|
@@ -2653,6 +3314,7 @@ export {
|
|
|
2653
3314
|
Applications,
|
|
2654
3315
|
AriClient,
|
|
2655
3316
|
Asterisk,
|
|
3317
|
+
BridgeInstance,
|
|
2656
3318
|
Bridges,
|
|
2657
3319
|
ChannelInstance,
|
|
2658
3320
|
Channels,
|