Resultado da chamada
Cartão de resultado da chamada
Após o término da conversa, os gestores podem receber a opção de fornecer um resultado da chamada. A janela modal de resultado da chamada exibe informações sobre o chamador e inclui opções para selecionar a entidade (lead, contato ou empresa) à qual a chamada será associada, anexar a gravação da chamada, adicionar uma tarefa com horário específico e deixar anotações sobre a conversa, caso existam. Se o gestor não atender a chamada, um registro com duração zero pode ser salvo. A aparência da janela de resultado da chamada é controlada pela parte JS do widget, enquanto a atualização real da chamada é feita pelo backend da integração.
Exemplo da janela de resultado da chamada:
A janela modal pode consistir nos seguintes elementos:
- Nome da janela: Resumo da chamada
- Entidade vinculada (lead/contato/empresa)
- Título do lead, que é um campo com propriedade de busca para localizar o lead/cliente adequado
- Painel da gravação da chamada (indicando a duração da chamada e um botão de reprodução)
- Horário e anotação para uma tarefa
- Botões Salvar ou Cancelar
Template para renderizar sua janela modal de Resultado da chamada:
import Modal from "lib/components/base/modal";
import markup from "./markup.js";
const appendCallResultModal = () => {
/**
* Você pode usar a classe Modal para abrir uma janela modal.
*/
const modal = new Modal({
class_name: "modal-window",
init: function ($modal_body) {
$modal_body
/**
* Aciona a exibição da janela modal.
*/
.trigger("modal:loaded")
/**
* Exibe seu markup.
*/
.html(markup)
/**
* Configura a janela modal.
*/
.trigger("modal:centrify");
},
destroy: () => {},
});
/**
* Aqui entra a lógica da sua janela modal.
*/
};Após selecionar a entidade e preencher os campos, você deve salvar o resultado da chamada, atualizando a chamada ao alterar o vínculo para a entidade escolhida. Para isso, uma tarefa é criada e adicionada à fila.
Vamos considerar o seguinte cenário de exemplo:
-
Uma chamada de um contato existente é realizada.
-
Um webhook sobre o término da chamada é disparado pelo serviço VoIP. Em seguida, o backend da integração precisa encontrar a entidade apropriada para vincular a chamada ou criar um Lead de Entrada caso nenhuma entidade conectada ao número exista em sua conta da Kommo.
-
O gestor preenche o modal de resultado da chamada e seleciona uma entidade diferente daquela sugerida pelo algoritmo. A anotação deve ser vinculada à entidade selecionada, e o vínculo com a entidade sugerida anteriormente deve ser removido pela integração.
-
O backend da integração VoIP então envia uma Nota de Chamada para vincular a nota ao cartão do contato selecionado.
Como mencionado no artigo Registro de chamadas, todas as chamadas devem ser recuperadas do serviço VoIP e armazenadas no repositório de chamadas dentro do banco de dados da integração. Também implementamos uma função para recuperar uma chamada com base em seus identificadores:
public static function getByCallIdAndKommoAccountId(string $callId, int $kommoAccountId): VoipCalls {
return VoipCalls::query()
->where('call_id', '=', $callId)
->where('kommo_account_id', '=', $kommoAccountId)
->first();
}Uma tarefa para atualizar a chamada de acordo com as informações vindas da janela modal de resultado da chamada:
public function __construct(
private int $kommoAccountId,
private string $callId,
private int $entityId,
private string $entityType
);Um exemplo do caso de uso responsável por atualizar a chamada conforme as informações vindas da janela modal de resultado da chamada é explicado aqui:
public function handle(UpdateFromModalTask $task): void
{
$widgetSettings = WidgetSettingsRepository::getByKommoAccountId($task->getKommoAccountId());
$voipCall = VoipCalls::getByCallIdAndKommoAccountId(
$task->getCallId(),
$widgetSettings->getKommoAccountId()
);
$call = Call::fromModel($voipCall);
if ($record = $voipCall->getRecording() ?? '') {
$record = sprintf(
'%s/voip/%s/get_call_record/%s',
$this->appConfig->getBaseUrl(),
$widgetSettings->getKommoAccountId(),
$voipCall->getCallId()
);
}
$call->setRecordLink($record);
$unsorted = null;
/** Se a chamada vem de leads de entrada:
* a) Se o gerente escolhe a entidade Lead, você precisa vincular um contato a ele;
* b) Se o gerente escolhe a entidade de Contato/Empresa, você precisa pegar à partir do contato no lead de entrada.
*/
if ($voipCall->getUnsortedUid() !== null) {
$unsorted = $this->unsortedService
->findUnsortedByUid($voipCall->getUnsortedUid());
$unsortedContact = $unsorted->getContacts()?->first();
$entityType = KommoEntityType::tryFrom($task->getEntityType());
switch ($entityType) {
case KommoEntityType::LEADS:
if ($unsortedContact !== null) {
$this->contactService
->linkContact($unsortedContact, $entityType, $task->getEntityId());
}
break;
case KommoEntityType::CONTACTS:
case KommoEntityType::COMPANIES:
// in the incoming lead only the contact id. We'll get all the information.
if ($unsortedContact !== null) {
$unsortedContact = $this
->contactService>getContactById($unsortedContact->getId());
$phones = $unsortedContact
->getCustomFieldsValues()
?->getBy('fieldCode', 'PHONE');
$newPhone = $phones?->getValues()?->first()->getValue();
if (!empty($newPhone)) {
$this->contactService->updatePhones($task->getEntityId(), $entityType, (string)$newPhone);
}
}
break;
default:
throw UnsupportedEntityException::create();
}
}
$this->contactService->updateCallEvent(
$call,
$voipCall->getResponsibleUserId() ?? self::BOT_USER_ID,
$voipCall->getEntityId(),
$voipCall->getEntityType(),
$task->getEntityId(),
$task->getEntityType()
);
// Rejeite leads de entrada após recusar a nota de chamada
if ($unsorted) {
$this->unsortedService->declineUnsorted($unsorted);
$voipCall->setUnsortedUid(null);
}
$voipCall->getEntityType())->save();
$task->setSuccess($success);
}A tarefa é criada dentro do worker, que obtém a tarefa da fila e a envia para o handler:
public function run(array $data, LoggerInterface $logger): void
{
$taskData = $data['data'] ?? [];
$task = new UpdateFromModalTask(
$taskData['account_id'],
$taskData['call_id'],
$taskData['entity_id'],
$taskData['entity_type']
);
$this->updateFromModalUseCase->handle($task);
if (!$task->isSuccess()) {
throw BusinessLogicException::create('Atualize o evento de erro de chamada');
}
}Nota de chamada
O registro de chamadas é realizado criando eventos dentro da entidade correspondente, categorizados sob tipos específicos de eventos para chamadas realizadas e recebidas.
Se o sistema VoIP oferecer suporte à gravação de chamadas, a interface pode exibir uma URL e um player de áudio para reproduzir a gravação. Para que o player funcione corretamente, o servidor que hospeda o arquivo deve incluir o cabeçalho HTTP Accept-Ranges: bytes na resposta. Se esse cabeçalho estiver ausente, funcionalidades como a de avançar e retroceder na gravação podem não funcionar corretamente.
Uma nota de chamada pode ser adicionada usando o endpoint POST /api/v4/calls. O endpoint faz uma busca automática por entidades usando o número de telefone fornecido e anexa o registro da chamada à entidade apropriada conforme o algoritmo definido. A documentação detalhada desse algoritmo está disponível na especificação do método. Se nenhuma entidade corresponder ao número informado, a chamada não será vinculada a nenhuma entidade.
O sistema oferece diferentes maneiras de exibir informações de chamadas, dependendo da direção da chamada (recebida ou realizada).
Updated about 10 hours ago
