Dados de chamadas do serviço VoIP
No capítulo anterior, abordamos os eventos que podem ocorrer ao final de uma chamada:
- Nesse tutorial, nós consideramos que a integração receberá um webhook do serviço VoIP assim que a chamada acabar. Um webhook sobre o fim da chamada é enviado à partir do serviço VoIP. Em seguida, o backend da integração precisa encontrar a entidade correspondente (lead, contato ou empresa), ou criar um lead de entrada caso não exista uma entidade vinculada ao número na sua conta Kommo.
- Caso exista um contato vinculado ao telefone, você pode adicionar uma nota de chamada com todos os dados necessários da chamada, e vincular com o contato existente via API.
- Se a chamada foi encerrada à partir da Kommo, você pode exibir uma janela modal para que um gerente adicione dados adicionais no resultado da chamada. Se um gerente adicionar os dados da chamada e salvar o resultado, os dados são passados ao backend do serviço VoIP, que por sua vez cria uma nota de chamada no cartão da entidade relacionada.
Como mencionamos anteriormente, esses eventos não são obrigatórios e exibem um exemplo de Integração VoIP na Kommo.
Ao receber um webhook indicando o fim de uma chamada, é necessário fazer o registro de todas as informações de chamada relevantes. Porém, para evitar a criação de registros duplicados, nós devemos primeiro checar se a chamada já existe. Caso a chamada não seja encontrada, nós usaremos a API de Registro de cahamadas para criar um novo registro. Caso a chamada já exista, nós iremos simplesmente atualizar a nota de chamada associada por meio de uma requisição de Edição de nota.
Para implementar o registro de chamadas, nós criamos uma classe para coletar todas as chamadas do serviço VoIP, e registrar elas em um repositório de chamadas no banco de dados da integração.
public static function getByCallIdAndKommoAccountId(string $callId, int $kommoAccountId): VoipCalls {
return VoipCalls::query()
->where('call_id', '=', $callId)
->where('kommo_account_id', '=', $kommoAccountId)
->first();
}Para cada chamada, uma tarefa é criada — tanto para salvar uma nova chamada (SaveCallEventTask), ou para atualizar uma tarefa existente (UpdateCallTask). O construtor de tarefas é assim:
public function __construct(
private int $kommoAccountId,
private string $toPhone,
private string $fromPhone,
private string $callId,
private CallType $direction,
private int $status,
private int $duration,
private int $startedAt,
private int $userId,
private ?string $recording = null
);public function __construct(
private int $kommoAccountId,
private string $callId,
);Aqui, dois casos de uso são definidos:
um para salvar uma nova chamada (SaveCallEventUseCase), e outro para atualizar uma chamada existente (UpdateCallUseCase). Cada caso de uso recebe a tarefa como um input e envia à fila correspondente para ser processada:
public function handle(SaveCallEventTask $task): void
{
$voipCall = VoipCalls::getByCallIdAndkommoAccountId(
$task->getCallId(),
$task->getKommoAccountId()
);
$isNew = $voipCall === null;
$voipCall = $isNew ? VoipCalls::create() : $voipCall;
$responsibleUser = $voipCall->getResponsibleUserId() ?? task->$getUserId();
$voipCall
->setDirection($task->getDirection())
->setCallId($task->getCallId())
->setkommoAccountId($task->getkommoAccountId())
->setToNumber($task->getToPhone())
->setFromNumber($task->getFromPhone())
->setDuration($task->getDuration())
->setStartedAt($task->getStartedAt())
->setResponsibleUserId($responsibleUser)
->setRecording($task->getRecording());
$voipCall->getStatus() ?: $voipCall->setStatus($task->getStatus());
$voipCall->save();
// Nós tentaremos fazer o registro da chamada com informações básicas
// chamando o AddCallWorker do caso de uso
// de registro de chamadas AddCallUseCase
$data = [
'call_id' => $task->getCallId(),
'account_id' => $task->getkommoAccountId(),
];
$queueName = $isNew ? AddCallWorker::QUEUE_NAME : UpdateCallNoteWorker::QUEUE_NAME;
$queueTask = new QueueTask($queueName, $data);
$this->queue->send($queueTask);
$task->setSuccess(true);
}public function handle(UpdateCallTask $task): void
{
$voipCall = voipCalls::getByCallIdAndKommoAccountId(
$task->getCallId(),
$task->getKommoAccountId()
);
$call = Call::fromModel($voipCall); // Entidade de chamada
if ($record = $voipCall->getRecording() ?? '') {
$record = sprintf(
'%s/voip/%s/get_call_record/%s',
$this->appConfig->getBaseUrl(),
$task->getKommoAccountId(),
$call->getCallId()
);
}
$call->setRecordLink($record);
// Chame nossa API de cliente para criar um evento de chamada
$this->callService->updateCallEvent(
$call,
$voipCall->getResponsibleUserId() ?? self::BOT_USER_ID,
$voipCall->getEntityId(),
$voipCall->getEntityType(),
$voipCall->getParentId()
);
$voipCall->setDelivered(DeliveryStatus::COMPLETED);
$voipCall->save();
$task->setSuccess(true);
}Ao salvar um evento de chamada. nos podemos criar uma nova chamada usando o ProcessCallWebhookWorker, ou atualizar uma existente através do UpdateCallNoteWorker. Vamos definir os dois workers:
public function run(array $data, LoggerInterface $logger): void
{
$taskData = $data['data'];
$webhookData = FromWebhook::fromArray($data['data']['webhook_data']);
$call = [
'call_id' => $webhookData->getCallId() ?? (string)$webhookData->getSessionId(),
'to_number' => $webhookData->getToPhoneNumber(),
'from_number' => $webhookData->getFromPhoneNumber(),
'direction' => $webhookData->getDirection(),
'duration' => 0,
'call_result' => 'done',
'recording' => null,
'started_at' => $webhookData->getStartTime()->toIso8601ZuluString()
];
$call = Call::fromArray($call);
$user = VoipUsers::getByExtensionId($webhookData->getExtension());
$call->setResponsibleUser($user->getUserId())
$task = new SaveCallEventTask(
(int)$taskData['kommo_account_id'],
$call->getToNumber(),
$call->getFromNumber(),
$call->getCallId(),
$call->getCallType(),
$call->getDirection(),
$call->getDuration(),
$call->getStartedAt(),
$call->getResponsibleUser(),
null
);
$this->saveCallEventUseCase->handle($task);
if (!$task->isSuccess())
{
throw BusinessLogicException::create('Erro ao salvar evento de chamada');
}
} public function run(array $data, LoggerInterface $logger): void
{
$taskData = $data['data'];
$task = new UpdateCallTask($taskData['account_id'], $taskData['call_id']);
$this->updateCallUseCase->handle($task);
if (!$task->isSuccess())
{
throw BusinessLogicException::create('Erro na chamada de atualização');
}
} Adicionalmente, nós devemos gerenciar os webhooks recebidos e submeter a tarefa correspondente apropriada para a fila.
public function handle(ServerRequestInterface $request): ResponseInterface
{
$data = $request->getParsedBody();
if ($data) {
$callWebhook = FromWebhook::fromArray($data);
$queueTask = new QueueTask(
ProcessCallWebhookWorker::QUEUE_NAME,
[
'kommo_account_id' => (int)$data['kommo_account_id'],
'webhook_data' => $callWebhook->toArray(),
]
);
$this->queue->send($queueTask);
}
}Updated about 9 hours ago
