Interação com o servidor Asterisk a partir de um aplicativo Java

Interagindo com um servidor Asterisk a partir de um aplicativo java através da Interface de Gerenciamento do Asterisk (AMI)


Se você está apenas começando uma pesquisa nessa área, a interação com este servidor pode parecer um pouco confusa, como me pareceu uma vez.

Para não procurar as informações necessárias nos fóruns no estilo de resposta-pergunta, estou anexando um pequeno tutorial sobre como interagir com o servidor Asterisk a partir de java.

Importante: Presumo que, uma vez que você tenha atingido o estágio de escrever código, já tenha um servidor Asterisk que possa acessar.

1) O que escolher para facilitar o trabalho?

Definitivamente - AMI (Asterisk Managment Interface): essa interface possui uma gama completa de funções que permitem fazer uma chamada, ouvir eventos em tempo real do servidor, receber um status de chamada e interrompê-lo, se necessário.

2) Qual biblioteca conectar?

Este aqui:

<dependency> <groupId>org.asteriskjava</groupId> <artifactId>asterisk-java</artifactId> <version>2.0.4</version> </dependency> 

3) Quais configurações precisam ser exibidas no servidor?

extensions.conf - a configuração que descreve o diaplan. Você estará constantemente em contato com ele. Se em um idioma mais compreensível, ele conterá scripts sobre o que o servidor fará quando receber uma chamada para um número específico. Primeiro, um contexto específico é pesquisado no diaplan - ele é escrito entre colchetes, após o qual um número é pesquisado sob a tag desse contexto pelo qual você está entrando em contato.

manager.conf - config com usuário e senha para o seu servidor Asterisk

O conteúdo dessa configuração deve ser aproximadamente o seguinte:

 [user_name] secret = password read = all write = all deny=0.0.0.0/0.0.0.0 permit=0.0.0.0/255.255.255.0 

  • nome_do_usuário - nome de usuário
  • segredo - senha para ele
  • negar - endereços IP cujo acesso é negado neste usuário
  • permissão - acesso ao qual é permitido. Certifique-se de especificar o IP com o qual está entrando em contato, pois o aster pode repelir sua solicitação.

sip.conf - todos os troncos são registrados aqui. Um tronco é um telefone do qual chamaremos um cliente.

4) Por onde começar a escrever código?

Existem duas opções: você precisa executar alguma ação no servidor Asterisk ou ouvir eventos no servidor. Nossa sequência inclui ambos.

Descrevemos o plano de ação:

  1. Abra a conexão com o servidor;
  2. Nós descrevemos o cenário do trabalho;
  3. Nós ouvimos eventos;
  4. Feche a conexão.

Assim, a conexão é inicializada durante a criação do objeto DefaultAsteriskServer:

 import org.asteriskjava.live.AsteriskServer; import org.asteriskjava.live.DefaultAsteriskServer; 

 AsteriskServer asteriskServer = new DefaultAsteriskServer(HOSTNAME, USERNAME, PASSWORD); asteriskServer.initialize(); 

Depois de abrir a conexão, precisamos ligar para o usuário. Vamos chamar isso de cenário de ação. A descrição do cenário estará em uma classe separada:

 /** *    */ public class ScenarioCall extends OriginateAction { private final Logger log = LoggerFactory.getLogger(ScenarioCall.class); private String TRUNK; private final String PHONE_FOR_RINGING; private final String EXTEN_FOR_APP; private final String CONTEXT_FOR_APP; public ScenarioCall(String trunk, String phoneForRinging, String extension, String context) { this.TRUNK = trunk; this.PHONE_FOR_RINGING = phoneForRinging; this.EXTEN_FOR_APP = extension; this.CONTEXT_FOR_APP = context; this.init(); } /** *         OriginateAction */ private void init() { //  String callId = ValidValues.getValidCallId(this.PHONE_FOR_RINGING); //    String channelAsterisk = ValidValues.getValidChannel(this.TRUNK, this.PHONE_FOR_RINGING); this.setContext(CONTEXT_FOR_APP); this.setExten(EXTEN_FOR_APP); this.setPriority(1); this.setAsync(true); this.setCallerId(callId); this.setChannel(channelAsterisk); log.info("Create Scenario Call: phone '{}',chanel '{}',context '{}',extension '{}'", callId, channelAsterisk, CONTEXT_FOR_APP, EXTEN_FOR_APP); } } 

O que precisamos entender nesse cenário? Primeiro, uma conexão de tronco é criada. Um tronco é o número a partir do qual você ligará para o assinante. Depois disso, é criada uma conexão entre o tronco e o assinante; depois, a conexão entre o assinante e quem mais você precisa está lá.

Está nessa ordem.

 this.setContext(CONTEXT_FOR_APP) 
O valor transmitido: o contexto no qual procuraremos o número de telefone ao qual você deseja associar o assinante (em extensions.conf).

 this.setExten(EXTEN_FOR_APP) 
Valor transmitido: o script que será executado depois que você entrar em contato com o assinante (a partir de extensions.conf).

 this.setCallerId(callId) 
Valor transmitido: número do assinante

 this.setChannel(channelAsterisk) 
Valor transmitido: canal de comunicação estabelecido, geralmente se parece com isso: trunk_name / phone_user.

Onde procurar trunk_name? Há uma configuração sip.conf no servidor asterisk - todos os troncos são registrados lá.

Crie uma chamada:

 if (asteriskServer .getManagerConnection().getState().equals(ManagerConnectionState.CONNECTED) || asteriskServer .getManagerConnection().getState().equals(ManagerConnectionState.CONNECTING) || asteriskServer .getManagerConnection().getState().equals(ManagerConnectionState.INITIAL)) { try { ScenarioCall scenarioCall = new ScenarioCall(trank, phone, extension, context); CallBack callBackForScenarioCall = new CallBack(); asteriskServer.originateAsync(scenarioCall, callBackForScenarioCall); } catch (ManagerCommunicationException e) { //   , StateConnection    RECONNECTING,     } } 

Criamos uma chamada, mas como rastrear dinamicamente?

Duas coisas são feitas para isso: uma instância da classe CallBack é passada no método originateAsync
e um ouvinte está pendurado no servidor, que mesclará tudo o que acontece conosco.

Um ouvinte é necessário porque a classe CallBack não notificará você sobre o final da chamada quando o usuário já tiver falado, nem notificará que o usuário ainda pode transferir para outro lugar.

 /** *        asteriskConnection.originateAsync  *  CallBack -     ,     *  originateAsync. CallBack      , *       ,    onNoAnswer ,  *     onBusy,     ,  onFailure,  . *        . ,      *      (      ,    ) */ public class CallBack implements OriginateCallback { /** *     PRERING,       * OriginateCallback -    */ private ChannelState resultCall = ChannelState.PRERING; /** *    ,   .         */ @Override public void onDialing(AsteriskChannel asteriskChannel) { //   ,  resultCall, //   asteriskChannel    null, //    resultCall   //   } /** *   .      *      6 - setStatus */ @Override public void onSuccess(AsteriskChannel asteriskChannel) { //   , asteriskChannel   null, // asteriskChannel.getState()      ChannelState.UP //   } /** *      ,    *      7 - setStatus () */ @Override public void onNoAnswer(AsteriskChannel asteriskChannel) { //   , //   asteriskChannel    null, //    resultCall   //   } /** *   *      7 - setStatus () */ @Override public void onBusy(AsteriskChannel asteriskChannel) { //   , //   asteriskChannel    null, //    resultCall   //   } /** *      */ @Override public void onFailure(LiveException e) { //     , //     , // onFailure      } } 

Como pendurar um ouvinte em um asterisco?

Para fazer isso, crie uma classe de implementação AsteriskServerListener, PropertyChangeListener.
Para a conexão criada, por meio de uma instância da classe AsteriskConnection, implementamos:

  this.asteriskConnection.addAsteriskServerListener(this.callBackEventListener); 

this.callBackEventListener - uma instância da nossa classe ouvinte, nascida de:

 ** *    Asterisk *  PropertyChangeListener   ,     . *  AsteriskServerListener   ,     AsteriskConnection. */ public class CallBackEventListener implements AsteriskServerListener, PropertyChangeListener { public void onNewAsteriskChannel(AsteriskChannel channel) { channel.addPropertyChangeListener(this); } public void onNewMeetMeUser(MeetMeUser user) { user.addPropertyChangeListener(this); } public void onNewQueueEntry(AsteriskQueueEntry user) { user.addPropertyChangeListener(this); } public void onNewAgent(AsteriskAgent asteriskAgent) { asteriskAgent.addPropertyChangeListener(this); } /** *    .   {@link PropertyChangeEvent} *    , *        , *        CallBack * * @param propertyChangeEvent      */ public void propertyChange(PropertyChangeEvent propertyChangeEvent) { findEventEndCall(propertyChangeEvent); } private void findEventEndCall(PropertyChangeEvent event) { if (event.getSource() instanceof AsteriskChannel) { AsteriskChannel callBackChannel = (AsteriskChannel) event.getSource(); String callId = getStringWithOnlyDigits(callBackChannel.getCallerId().toString()); callId = ValidValues.getValidCallId(callId); if (callBackChannel.getState().toString().equals("HUNGUP") && event.getOldValue().toString().contains("RINGING")) { //      callBackChannel.removePropertyChangeListener(this); //     } else if (callBackChannel.getState().toString().equals("HUNGUP") && event.getOldValue().toString().contains("UP")) { //     callBackChannel.removePropertyChangeListener(this); //     } else if (callBackChannel.getState().toString().equals("HUNGUP")) { //      callBackChannel.removePropertyChangeListener(this); //     } } } private String getStringWithOnlyDigits(String strForParse) { String result = ""; if (strForParse != null && !strForParse.isEmpty()) { CharMatcher ASCII_DIGITS = CharMatcher.anyOf("<>").precomputed(); result = ASCII_DIGITS.removeFrom(strForParse.replaceAll("[^0-9?!]", "")); } return result; } } 

Aconselho você desde o início, apenas para se comprometer com o que diz respeito ao propertyChange e analisar o PropertyChangeEvent; será um monte de tudo o que acontece no servidor. Ele não filtra informações. Portanto, a conclusão: pendure o ouvinte o mínimo possível. Não é para todas as chamadas, porque isso pode ser feito mesmo na classe OriginateCallback, até onde eu achei. Isso é inútil. Veja quais objetos PropertyChangeEvent chegam até você, veja os tipos de campos e quais você precisa. Próximo - bem-vindo ao mundo do processamento de informações.

Um pouco sobre validação de dados.

Em OriginateAction.setChannel - o nome do tronco / usuário do telefone é passado
phone_user - se for russo, deve começar com um número oito, se o número internacional estiver com um sinal de mais.

Em OriginateAction.setCallerId - o número de telefone do cliente é transmitido,
em CallBackEventListener, ele entrará em callBackChannel.getCallerId ().

Vai ficar assim:

 String callId = getStringWithOnlyDigits(callBackChannel.getCallerId().toString()); 

No final, não esqueça:

 asteriskServer.shutdown(); 

Se você precisar interromper qualquer chamada, na classe CallBackEventListener
para o canal de comunicação existente, realizamos:

 callBackChannel.hangup(); 

Um tutorial tão simples acabou. À primeira vista, é claro, é muito simples, mas acredite, leva muito tempo e nervos para encontrar informações, adiar todos os métodos e sair do trabalho.

Boa sorte com seus servidores Asterisk!

Leitura adicional:

1) Tutorial Asterisk-Java

2) Interface de gerenciamento de asterisco (AMI)

Source: https://habr.com/ru/post/pt469491/


All Articles