Stwórz kampanię wychodzącą z automatycznymi komunikatami.
Wykorzystaj 3CX Call Control API do automatyzacji kampanii połączeń wychodzących. Połącz listę numerów telefonów z IVR, odtwórz automatyczny komunikat i przekieruj połączenia do różnych miejsc docelowych w zależności od wyboru w menu IVR. W przeciwieństwie do tradycyjnego systemu wybierania numerów, ta metoda zapewnia elastyczność w zakresie komunikatów, trasowania połączeń oraz integracji z CRM lub bazą danych. Czytaj dalej, aby dowiedzieć się więcej i rozpocząć konfigurację.
Kiedy stosować skrypt kampanii wychodzącej z automatycznym komunikatem

Anulowanie lotu to doskonały przykład zastosowania. Linia lotnicza może powiadomić pasażerów za pomocą nagranego komunikatu i umożliwić im połączenie z działem obsługi poprzez opcje w menu.
To podstawowy przypadek użycia, ale można go rozbudować. Możesz stworzyć własny IVR obsługujący wejścia DTMF oraz kontrolę strumienia audio dla kampanii wychodzących.
Zobacz oficjalne repozytorium 3CX na GitHubie, aby poznać więcej przykładów.
Konfiguracja obsługi połączeń i integracja API
Utwórz IVR w 3CX, dodaj go do dostępu Call Control API i wybierz go z listy rozszerzeń.
Inicjowanie połączenia
Interfejs użytkownika klienta umożliwia wprowadzenie listy numerów oddzielonych przecinkami w prostym polu tekstowym.
const destinations = source
.split(',')
.map((num) => num.trim())
.filter(Boolean);
Struktura kolejki obsługuje połączenia jedno po drugim. Nieodebrane lub zajęte połączenia mogą zostać dodane do kolejki do ponownego wybierania.
destinations.forEach((destNumber) => this.callQueue.enqueue(destNumber));
Logika wybierania
Funkcja poniżej pobiera pierwszy numer z kolejki i rozpoczyna jego przetwarzanie.
public async makeCallsToDst() {
if (this.callQueue.isEmpty()) return;
const destNumber = this.callQueue.dequeue();
// …
Przed wykonaniem połączenia system sprawdza połączenie z PBX oraz upewnia się, że źródłowe rozszerzenie nie jest aktualnie używane.
if (!this.sourceDn || !this.externalApiSvc.connected) {
if (destNumber)
this.failedCalls.push({
callerId: destNumber,
reason: NO_SOURCE_OR_DISCONNECTED,
});
return;
}
const participants = this.getParticipantsOfDn(this.sourceDn);
if (participants && participants.size > 0) {
if (destNumber)
this.failedCalls.push({
callerId: destNumber,
reason: CAMPAIGN_SOURCE_BUSY,
});
return;
}
//…
Wykonywanie połączenia
Połączenie jest nawiązywane przy użyciu pierwszego dostępnego urządzenia.
Listę dostępnych urządzeń dla konkretnego DN można znaleźć w sekcji State of The Call Control.
try {
const source = this.fullInfo?.callcontrol.get(this.sourceDn);
const device: DNDevice | undefined = source?.devices?.values().next().value;
if (!device?.device_id) {
throw new BadRequest('Devices not found');
}
const response = await this.externalApiSvc.makeCallFromDevice(
this.sourceDn,
encodeURIComponent(device.device_id),
destNumber,
);
//…
Metoda makeCallFromDevice korzysta z tego endpointu:
public makeCallFromDevice(source: string, deviceId: string, dest: string) {
const url = '/callcontrol' + `/${source}` + '/devices' + `/${deviceId}` + '/makecall';
return this.fetch!.post(
url,
{
destination: dest,
},
{
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
},
);
}
Obsługa błędów
Jeśli PBX zaakceptuje żądanie, identyfikator połączenia zostanie zapisany. W przeciwnym razie zostanie zarejestrowany błąd.
if (response.data.result?.id) {
this.incomingCallsParticipants.set(response.data.result.id, response.data.result);
} else {
this.failedCalls.push({
callerId: destNumber!,
reason: response?.data?.reasontext || UNKNOWN_CALL_ERROR,
});
}
//…
Błędy występujące między aplikacją a PBX są obsługiwane tutaj:
//...
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
this.failedCalls.push({
callerId: destNumber!,
reason: error.response?.data.reasontext || UNKNOWN_CALL_ERROR,
});
} else {
this.failedCalls.push({
callerId: destNumber!,
reason: UNKNOWN_CALL_ERROR,
});
}
}
Obsługa zdarzeń uczestników
Połączenie WebSocket śledzi status IVR, inicjuje nowe połączenia i zarządza uczestnikami.
Szczegóły dotyczące struktury zdarzeń WebSocket oraz innych powiązanych aspektów znajdziesz w tym przewodniku.
private wsEventHandler = (json: string) => {
try {
const wsEvent: WSEvent = JSON.parse(json);
if (!this.externalApiSvc.connected || !wsEvent?.event?.entity) {
return;
}
const { dn, type } = determineOperation(wsEvent.event.entity);
//...
Gdy nastąpi aktualizacja, aplikacja pobiera i zapisuje nowe dane.
case EventType.Upset:
{
this.externalApiSvc
.requestUpdatedEntityFromWebhookEvent(wsEvent)
.then((res) => {
const data = res.data;
set(this.fullInfo, wsEvent.event.entity, data); // update local state
if (dn === this.sourceDn) {
if (type === PARTICIPANT_TYPE_UPDATE) {
/**
* handle here update of participants
*/
}
}
})
.catch((err) => {
if (axios.isAxiosError(err)) {
console.error(`AXIOS ERROR code: ${err.response?.status}`);
} else console.error('Unknown error', err);
});
}
break;
Możemy użyć tego URL-a, aby zażądać zaktualizowanej encji i przeprowadzić przyrostową aktualizację stanu w naszej aplikacji (zobacz DN Update Request).
public requestUpdatedEntityFromWebhookEvent(ws: WSEvent) {
return this.fetch.get(ws.event.entity);
}
Gdy uczestnik zostanie usunięty, kampania jest kontynuowana.
case EventType.Remove: {
const removed = set(this.fullInfo, wsEvent.event.entity, undefined);
if (dn === this.sourceDn) { // update related to our campaign handler
if (type === PARTICIPANT_TYPE_UPDATE) {// update related to call participant
/**
* handle here removed participants
*/
if (removed?.id) {
//...
if (!participants || participants?.size < 1) { // Handler is free
this.makeCallsToDst(); // continue with campaign
}
}
}
}
}
Możemy użyć ten event handler w ramach WebSocket event listener.
ws.on('message', (buffer) => {
const message = decoder.decode(buffer as Buffer);
wsEventHandler(message);
});
Więcej skryptów przepływu połączeń
Na naszej stronie internetowej znajdziesz kolekcję skryptów przepływu połączeń. Sprawdź je i zobacz, jak możesz zautomatyzować 3CX zgodnie ze swoimi potrzebami.



