التفاعل مع خادم النجمة من تطبيق جافا

التفاعل مع خادم Asterisk من تطبيق java من خلال واجهة Asterisk Managment (AMI)


إذا كنت تبدأ البحث في هذا المجال ، فقد يبدو التفاعل مع هذا الخادم مربكًا إلى حد ما ، كما بدا لي من قبل.

لكي لا ابحث عن الأجزاء الضرورية من المعلومات في المنتديات بأسلوب الإجابة على الأسئلة ، أرفق برنامج تعليمي صغير حول التفاعل مع خادم Asterisk من java.

هام: أفترض أنه بمجرد وصولك إلى مرحلة كتابة التعليمات البرمجية ، يكون لديك بالفعل خادم Asterisk فعال يمكنك الوصول إليه.

1) ماذا تختار لجعلها مريحة للعمل؟

بالتأكيد - واجهة إدارة النجمة (AMI): تحتوي هذه الواجهة على مجموعة كاملة من الوظائف التي تتيح لك إجراء مكالمة ، والاستماع إلى الأحداث في الوقت الفعلي من الخادم ، وتلقي حالة الاتصال ومقاطعتها إذا لزم الأمر.

2) ما مكتبة للاتصال؟

هذا واحد:

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

3) ما التكوينات تحتاج إلى عرضها على الخادم؟

extensions.conf - التكوين الذي يصف diaplan. سوف تكون باستمرار الاتصال به. إذا كان بلغة أكثر قابلية للفهم ، فهو يحتوي على نصوص لما سيفعله الخادم عندما يتلقى مكالمة إلى رقم محدد. أولاً ، يتم البحث في سياق معين في diaplan - إنه مكتوب بين قوسين معقوفين ، وبعد ذلك يتم البحث عن رقم تحت علامة هذا السياق الذي تتصل به.

manager.conf - التكوين مع المستخدم وكلمة المرور لخادم النجمة الخاص بك

يجب أن يكون محتوى هذا التكوين تقريبًا كما يلي:

 [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 

  • اسم المستخدم - اسم المستخدم
  • سر - كلمة المرور لذلك
  • رفض - عناوين IP التي تم رفض الوصول تحت هذا المستخدم
  • تصريح - الوصول إلى ما هو مسموح به. تأكد من تحديد عنوان IP الذي تتصل به في التصريح ، نظرًا لأن النجم يمكنه صد طلبك.

sip.conf - جميع جذوعها مسجلة هنا. الجذع هو الهاتف الذي سنتصل منه بالعميل.

4) من أين تبدأ كتابة التعليمات البرمجية؟

هناك خياران: إما أن تحتاج إلى القيام ببعض الإجراءات على خادم Asterisk ، أو الاستماع إلى الأحداث على الخادم. ويشمل تسلسل لدينا على حد سواء.

وصفنا خطة العمل:

  1. افتح الاتصال بالخادم
  2. وصفنا سيناريو العمل ؛
  3. نستمع إلى الأحداث.
  4. أغلق الاتصال.

وفقًا لذلك ، تتم تهيئة الاتصال أثناء إنشاء كائن DefaultAsteriskServer:

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

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

بعد فتح الاتصال ، نحتاج إلى الاتصال بالمستخدم. سوف نسمي هذا سيناريو العمل. سيكون وصف السيناريو في فصل منفصل:

 /** *    */ 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); } } 

ماذا نحتاج أن نفهم في هذا السيناريو؟ أولاً ، يتم إنشاء اتصال جذع. الجذع هو الرقم الذي سوف تتصل منه بالمشترك. بعد ذلك ، يتم إنشاء اتصال بين الجذع والمشترك ، وبعد ذلك الاتصال بين المشترك والذي تحتاجه هو الآخر.

إنه بهذا الترتيب.

 this.setContext(CONTEXT_FOR_APP) 
القيمة المنقولة: هي السياق الذي سنبحث فيه عن رقم الهاتف الذي تريد ربط المشترك به (من extensions.conf).

 this.setExten(EXTEN_FOR_APP) 
القيمة المنقولة: البرنامج النصي الذي سيتم تنفيذه بعد الاتصال بالمشترك (من extensions.conf).

 this.setCallerId(callId) 
القيمة المنقولة: رقم المشترك لدينا

 this.setChannel(channelAsterisk) 
القيمة المنقولة: قناة اتصال قائمة ، عادة ما تبدو كما يلي: trunk_name / phone_user.

أن ننظر فيها ل trunk_name؟ هناك تكوين sip.conf على خادم العلامة النجمية - يتم تسجيل جميع الأمتعة هناك.

إنشاء مكالمة:

 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,     } } 

أنشأنا مكالمة ، ولكن كيف لتتبع ذلك بشكل حيوي؟

يتم إجراء شيئين لهذا: يتم تمرير مثيل لفئة CallBack في طريقة originateAsync
ويتم تعليق المستمع على الخادم ، والذي سيدمج كل ما يحدث لنا.

هناك حاجة إلى مستمع لأن فئة CallBack لن تُعلمك بنهاية المكالمة عندما يكون المستخدم قد تحدث بالفعل ، ولن يخطرك بأنه لا يزال بإمكان المستخدم النقل في مكان آخر.

 /** *        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      } } 

كيفية تعليق المستمع على النجمة؟

للقيام بذلك ، قم بإنشاء فئة تطبيق AsteriskServerListener ، PropertyChangeListener.
بالنسبة للاتصال الذي تم إنشاؤه ، من خلال مثيل لفئة AsteriskConnection ، نقوم بتنفيذ:

  this.asteriskConnection.addAsteriskServerListener(this.callBackEventListener); 

this.callBackEventListener - مثيل لفئة المستمع الخاصة بنا ، المولودة من:

 ** *    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; } } 

أنصحك في البداية بالتعهد ، ماذا يأتي إلى propertyChange وإلقاء نظرة على PropertyChangeEvent ، سيكون بمثابة جحيم كومة من كل شيء يحدث على الخادم. لا تصفية المعلومات على الإطلاق. لذلك ، الاستنتاج: شنق المستمع بأقل قدر ممكن. ليس لكل مكالمة ، لأنه يمكن القيام به حتى في فئة OriginateCallback ، بقدر ما وجدت. هذا لا طائل منه. انظر إلى ما تأتي به كائنات PropertyChangeEvent ، انظر إلى نوع الحقول هناك والحقول التي تحتاجها. التالي - مرحبا بكم في عالم معالجة المعلومات.

قليلا عن التحقق من صحة البيانات.

في OriginateAction.setChannel - يتم تمرير trunk_name / phone_user
phone_user - إذا كانت روسية ، فيجب أن تبدأ برقم ثمانية ، إذا كان الرقم الدولي زائدًا.

في OriginateAction.setCallerId - يتم إرسال رقم هاتف العميل ،
ثم في CallBackEventListener سوف يأتي في callBackChannel.getCallerId ().

سوف أعتبر مثل هذا:

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

في النهاية ، لا تنسى:

 asteriskServer.shutdown(); 

إذا كنت بحاجة إلى مقاطعة أي مكالمة ، فإما في فئة CallBackEventListener
لقناة الاتصال الحالية التي نقوم بها:

 callBackChannel.hangup(); 

تحولت مثل هذا البرنامج التعليمي البسيط. للوهلة الأولى ، بالطبع ، الأمر بسيط للغاية ، لكن صدقوني ، يستغرق الكثير من الوقت والأعصاب للعثور على المعلومات ، وتأجيل جميع الأساليب وترك العمل.

حظا سعيدا مع خوادم النجمة الخاصة بك!

مزيد من القراءة:

1) النجمة جافا تعليمي

2) واجهة إدارة العلامة النجمية (AMI)

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


All Articles