[Freeswitch] Originate

새로운 콜을 생성할 때 사용하는 모듈이다.

기본 메뉴얼. https://wiki.freeswitch.org/wiki/Mod_commands#originate

freeswitch@internal> originate
-USAGE: <call url> <exten>|&<application_name>(<app_args>) [<dialplan>] [<context>] [<cid_name>] [<cid_num>] [<timeout_sec>]

Parameters:

<call_url> : 전화를 걸고자 하는 URL 을 입력한다. PBX 서버의 IP 주소 혹은 Freeswitch 내부에 설정된 설정값의 항목을 입력한다.

목적지 주소(즉 전화를 걸고자하는 번호)는 다음 중 하나가 되어야 한다 :

<exten> Dailplan 에 입력된 Extension 번호.

&<application_name>(<app_args>) :

“&” 은 다음에 뒤따라 오는 값이 Application 속성인지/Exten 속성인지를 결정한다. 즉, “&” 가 없으면 Extension, 있으면 Application 으로 인식한다.

(<app_args>) 은 옵션이며 Application 에서 요구하는 항목에 따라 달라진다. 예를 들어 Park() Application 의 경우, 별도의 인자값을 요구하지 않는다.

사용가능한 Application 리스트: park, bridge, javascript/lua/perl, playback (remove mod_native_file), and many others.

Note: Use single quotes to pass arguments with spaces, e.g. ‘&lua(test.lua arg1 arg2)’

Note: There is no space between & and the application name

<dialplan> : 사용하고하는 Dailplan. 비어있을 경우, 기본값으로 ‘XML’를 사용한다.

<context> : 사용하고자 하는 Context. 비어있을 경우, 기본값으로 ‘default’를 사용한다.

<cid_name> : CallerID name

<cid_num> : CallerID number

<timeout_sec> : Timeout in seconds

 

Options:

이 Option 설정들은 ‘{ }’로 싸여져서 사용된다.
ex. “originate {ignore_early_media=true}sofia/example/user 8334”

또한, Option 설정들은 반드시 ‘,’ 로 구분되어야 한다.
ex. “originate {ignore_early_meida=true,originate_timeout=2}sofia/example/user 8334”

group_confirm_key

group_confirm_file

forked_dial

fail_on_single_reject

ignore_early_media

return_ring_ready

originate_retries

originate_retry_sleep_ms

originate_caller_id_name

originate_caller_id_number

originate_timeout

sip_auto_answer

[Asterisk] Section ‘test-01’ lacks type

Asterisk sip reload 중… 다음과 같은 오류가 나타났다.

[Jun  2 04:45:56] WARNING[19371]: chan_sip.c:28964 reload_config: Section ‘test-01’ lacks type
[Jun  2 04:45:56] WARNING[19371]: chan_sip.c:28964 reload_config: Section ‘test-02’ lacks type

Asterisk SIP 설정 중, type 설정이 빠져있어서 발생하는 오류였다.

type=friend

추가 후, 해결!

[Freeswitch] Making freeswitch python script

실행되는 기본 함수

Freeswitch 에서 Python 스크립트를 호출할 때, 호출하는 주체가 어디냐에 따라 자동으로 실행되는 Default 함수가 달라진다.

 

– Dialplan 내에서 <action application=”python” data=”mod_fstest.python_example”/> 와 같은 형식으로 호출되는 경우.

호출되는 스크립트 내, handler() 함수가 호출된다.

handler() 함수의 모습은

# APPLICATION
#
# default name for apps is "handler" it can be overridden with <modname>::<function>
# session is a session object
# args is all the args passed after the module name
def handler(session, args):

	consoleLog('info', 'Answering call from Python.n')

처럼 되어야 한다.

 

– fs_cli 등에서 python 명령으로 호출되는 스크립트의 경우.
호출되는 스크립트 내의 fsapi() 함수가 호출된다.

fsapi() 함수의 모습은

# FSAPI CALL FROM CLI, DP HTTP etc
#
# default name for python FSAPI is "fsapi" it can be overridden with <modname>::<function>
# stream is a switch_stream, anything written with stream.write() is returned to the caller
# env is a switch_event
# args is all the args passed after the module name
# session is a session object when called from the dial plan or the string "na" when not.
def fsapi(session, stream, env, args):

	stream.write("w00t!n" + env.serialize())

처럼 되어야 한다.

 

– fs_cli 등에서 pyrun 명령으로 호출되는 스크립트의 경우,

호출되는 스크립트 내의 runtime() 함수가 호출된다.

runtime() 함수의 모습은

# RUN A FUNCTION IN A THREAD
#
# default name for pyrun is "runtime" it can be overridden with <modname>::<function>
# args is all the args passed after the module name
def runtime(args):

	print args + "n"

처럼 되어야 한다.

 

지원 API

mod_python 모듈을 이용하여 스크립트 작성시, freeswitch 에서 제공하는 여러가지 API 들을 이용하여 편리하게 프로그래밍을 할 수 있다.
지원하는 API 내용은 mod_lua 와 똑같은 내용의 API 를 지원하며 자세한 기능 및 API 일람은 아래의 링크에서 확인할 수 있다.

mod_lua API 목록: https://wiki.freeswitch.org/wiki/Mod_lua

 

예제 python Script

예제로 사용한 python script 이다. 원본은 https://wiki.freeswitch.org/wiki/Mod_python#Sample_Python_Scripts 에서 구할 수 있다.

import os
from freeswitch import *

# HANGUP HOOK
#
# session is a session object
# what is "hangup" or "transfer"
# if you pass an extra arg to setInputCallback then append 'arg' to get that value
# def hangup_hook(session, what, arg):
def hangup_hook(session, what):

	consoleLog("info","hangup hook for %s!!nn" % what)
	return


# INPUT CALLBACK
#
# session is a session object
# what is "dtmf" or "event"
# obj is a dtmf object or an event object depending on the 'what' var.
# if you pass an extra arg to setInputCallback then append 'arg' to get that value
# def input_callback(session, what, obj, arg):
def input_callback(session, what, obj):

	if (what == "dtmf"):
		consoleLog("info", what + " " + obj.digit + "n")
	else:
		consoleLog("info", what + " " + obj.serialize() + "n")		
	return "pause"

# APPLICATION
#
# default name for apps is "handler" it can be overridden with <modname>::<function>
# session is a session object
# args is all the args passed after the module name
def handler(session, args):

	consoleLog('info', 'Answering call from Python.n')
	consoleLog('info', 'Arguments: %sn' % args)

	session.answer()
	session.setHangupHook(hangup_hook)
	session.setInputCallback(input_callback)
	session.execute("playback", session.getVariable("hold_music"))

# FSAPI CALL FROM CLI, DP HTTP etc
#
# default name for python FSAPI is "fsapi" it can be overridden with <modname>::<function>
# stream is a switch_stream, anything written with stream.write() is returned to the caller
# env is a switch_event
# args is all the args passed after the module name
# session is a session object when called from the dial plan or the string "na" when not.
def fsapi(session, stream, env, args):

	stream.write("w00t!n" + env.serialize())
	

# RUN A FUNCTION IN A THREAD
#
# default name for pyrun is "runtime" it can be overridden with <modname>::<function>
# args is all the args passed after the module name
def runtime(args):

	print args + "n"

# BIND TO AN XML LOOKUP
#
# default name for binding to an XML lookup is "xml_fetch" it can be overridden with <modname>::<function>
# params a switch_event with all the relevant data about what is being searched for in the XML registry.
#
def xml_fetch(params):

	xml = '''
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="freeswitch/xml">
  <section name="dialplan" description="RE Dial Plan For FreeSWITCH">
    <context name="default">
      <extension name="generated">
        <condition>
         <action application="answer"/>
         <action application="playback" data="${hold_music}"/>
        </condition>
      </extension>
    </context>
  </section>
</document>
'''

	return xml

 

예제로 사용한 dialplan 이다. “5555555”로 전화를 걸면 스크립트가 사용된다.

<include>
  <extension name="python_test">
    <condition field="destination_number" expression="^(5555555)$">
      <!--
      If you're hosting multiple domains you will want to set the
      target_domain on these calls so they hit the proper domain after you
      transfer the caller into the default context. 
      
      $${domain} is the default domain set from vars.xml but you can set it
      to any domain you have setup in your user directory.

      -->
      <action application="info"/>
      <action application="python" data="mod_fstest.python_example"/>
      <!--<action application="set" data="domain_name=$${domain}"/>-->
      <!-- This example maps the DID 5551212 to ring 1000 in the default context -->
      <!--<action application="transfer" data="1000 XML default"/>-->
    </condition>
  </extension>
</include>

 

작성한 Python 스크립트를 dialplan 에서 호출하기 위해서는, 당연하겠지만 Dialplan 내에서 어떤 스크립트를 호출할지를 지정해 주어야 한다.

<action application=”python” data=”foo.bar”/>

위에 입력한 foo.bar 는 foo package 에 있는 bar 모듈을 호출한다는 설정이다. 뒤에 확장자 .py 를 붙이지 않도록 주의하자.
이 경우, 사용하게 되는 함수는 ‘handler’ 함수이다.

2014-05-06 17:55:40.838121 [NOTICE] mod_python.c:212 Invoking py module: mod_fstest.python_example
2014-05-06 17:55:40.838121 [DEBUG] mod_python.c:281 Call python script 
2014-05-06 17:55:40.838121 [INFO] switch_cpp.cpp:1293 Answering call from Python.
2014-05-06 17:55:40.838121 [INFO] switch_cpp.cpp:1293 Arguments:

handler 함수.

# APPLICATION
#
# default name for apps is "handler" it can be overridden with <modname>::<function>
# session is a session object
# args is all the args passed after the module name
def handler(session, args):

	consoleLog('info', 'Answering call from Python.n')
	consoleLog('info', 'Arguments: %sn' % args)

	session.answer()
	session.setHangupHook(hangup_hook)
	session.setInputCallback(input_callback)
	session.execute("playback", session.getVariable("hold_music"))

인자 값으로 session 과 args 를 입력받는다.
session 은 python 스크립트 내에서 사용되는 freeswitch interface 객체이며, args 는 모듈 호출시 넘겨지는 인자값들이다.

 

또한, fs_cli 에서 바로 작성한 python 스크립트를 호출할 수 있다. 이 경우, 사용한 명령어에 따라 호출되는 함수가 달라지는데, python 명령으로 호출할 경우, 스크립트 내, ‘fsapi’ 함수가 바로 호출이 된다.

freeswitch@192.168.200.10@internal> python mod_fstest.python_example
w00t!
'Event-Name: API
Core-UUID: 86d48a48-6f22-475c-89ac-146e6d0217d9
FreeSWITCH-Hostname: MyDebian
FreeSWITCH-Switchname: MyDebian
FreeSWITCH-IPv4: 10.0.2.10
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2014-05-06%2017%3A43%3A01
Event-Date-GMT: Tue,%2006%20May%202014%2015%3A43%3A01%20GMT
Event-Date-Timestamp: 1399390981818146
Event-Calling-File: switch_loadable_module.c
Event-Calling-Function: switch_api_execute
Event-Calling-Line-Number: 2406
Event-Sequence: 4158
API-Command: python
API-Command-Argument: mod_fstest.python_example

'
2014-05-06 17:43:01.818146 [NOTICE] mod_python.c:212 Invoking py module: mod_fstest.python_example
2014-05-06 17:43:01.818146 [DEBUG] mod_python.c:281 Call python script 
2014-05-06 17:43:01.818146 [DEBUG] mod_python.c:284 Finished calling python script

 

참조 : https://wiki.freeswitch.org/wiki/Mod_python#Sample_Python_Scripts

 

 

[Freeswitch] Support language

Freeswitch 에서 제공하는 Dialplan 기능 중에서는 다른 언어를 사용할 수 있도록 해주는 기능들이 있다.

지원 가능한 언어 목록은 다음과 같다.

Languages

Languages for Call Control explained.

Languages (unsupported / out of tree)

특이할 점은 php 와 ruby 는 공식적으로는 지원하지 않는다는 점이다.

Dialplan 작성시, Freeswitch 에서 제공하는 Dialplan 기능뿐만 아니라, 다른 언어들까지 활용하여 Dialplan 을 작성한다면 보다 더 강력한 기능을 발휘할 수 있다.
당장 DB 연동만 생각해봐도 정말 강력하다는 것을 알 수 있을 것이다.

 

참조 : https://wiki.freeswitch.org/wiki/Modules

[Freeswitch] Enable python script.

Freeswitch-python module install & configuration

Debian Linux/Freeswitch source 설치 기준으로 설명한다.

Freeswitch Python 모듈인 mod_pythom 을 설치해야 한다.
Source Directory 로 이동하자.

$ cd /usr/local/src/freeswitch.git

Freeswitch 는 설치시 모든 모듈을 컴파일하지 않는다. 아무런 추가 설정이 없다면 기본 모듈만을 설치한다.
따라서, 다른 추가 모듈들이 필요하다면 별도의 설정 후, 다시 컴파일을 진행해 주어야 한다.
정확히는 컴파일 시, Source 디렉토리 내의 modules.conf 파일을 참조하여 컴파일을 해야하는 모듈들을 확인한 다음에 컴파일을 진행한다.
따라서, modules.conf 파일을 수정하면 추가적인 모듈 설치가 가능하다.
python module compile 을 활성화 시키기 위해서 modules.conf 파일을 수정한다.

$ vi modules.conf

mod_python 부분을 찾아서 주석 해제한다.

#languages/mod_python
languages/mod_python

make & make install.

$ make && make install

python-freeswitch 모듈이 제대로 설치되었는지 확인해야 한다.
아래의 예제처럼 python 시작후, 전체 모듈 검색에서 freeswitch 모듈이 확인되어야 한다.
만약 확인되지 않으면 수동으로 freeswitch 모듈을 옮겨 주어야 한다.

pchero@MyDebian:/etc$ python
Python 2.7.3 (default, Mar 13 2014, 11:03:55) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> help('modules')

ANSI                _codecs_iso2022     fdpexpect           pyatspi
ArgImagePlugin      _codecs_jp          feedparser          pyclbr
BaseHTTPServer      _codecs_kr          filecmp             pycurl
Bastion             _codecs_tw          fileinput           pydoc
BdfFontFile         _collections        fnmatch             pydoc_data
BeautifulSoup       _csv                formatter           pyexpat
BeautifulSoupTests  _ctypes             fpconst             pygtk
BmpImagePlugin      _ctypes_test        fpectl              pynotify
BufrStubImagePlugin _curses             fpformat            quopri
CDROM               _curses_panel       fractions           random
CGIHTTPServer       _dbus_bindings      freeswitch          re
Canvas              _dbus_glib_bindings ftplib              readline
ConfigParser        _elementtree        functools           reportbug
ContainerIO         _functools          future_builtins     reportlab
...

만약 확인되지 않는다면 수동으로 freeswitch 모듈을 설치해 주어야 한다.
아래의 예제는 Debian Linux/Python2.7 설치 기준이다.
리눅스 배포판/Python 버전에 따라 모듈 설치 디렉토리가 달라지므로 주의하자.

pchero@MyDebian:/usr/local/src/freeswitch.git/src/mod/languages/mod_python$ pwd
/usr/local/src/freeswitch.git/src/mod/languages/mod_python

pchero@MyDebian:/usr/local/src/freeswitch.git/src/mod/languages/mod_python$ sudo cp ./freeswitch.py /usr/lib/python2.7/dist-packages/

 

Python 에서 freeswitch 모듈을 확인했다면, 이제는 Freeswitch 에서 Python 모듈이 사용가능한지 확인해야 한다.

modules.conf.xml 파일에서 매번 자동으로 mod_python module 을 load 하도록 설정해주자.
<load_module=”mod_python”> 부분을 주석 해제하거나 추가해주자.

    <!-- Languages -->
    <!-- <load module="mod_spidermonkey"/> -->
    <load module="mod_v8"/>
    <!-- <load module="mod_perl"/> -->
    <load module="mod_python"/>
    <!-- <load module="mod_java"/> -->
    <load module="mod_lua"/>

freeswitch cli(fs_cli) 에서 mod_python 모듈을 load 하고, 정상적으로 load 되었는지 확인하자.

freeswitch@192.168.200.10@internal> load mod_python
+OK Reloading XML

freeswitch@192.168.200.10@internal> module_exists mod_python
true

Using python script on freeswitch.

이제 실제로 sample python script 를 작성 및 사용해 보자. mod_python 소스 디렉토리에는 훌륭한 예제 스크립트가 있다. 사용하도록 하자.

cp /usr/local/src/freeswitch.git/src/mod/languages/mod_python/python_example.py /usr/lib/pymodules/python2.7

마지막으로 실제 Dialplan 내에서 python 모듈을 호출하도록 설정해야 한다.

<include>
  <extension name="python_test">
    <condition field="destination_number" expression="^(5555555)$">
      <!--
      If you're hosting multiple domains you will want to set the
      target_domain on these calls so they hit the proper domain after you
      transfer the caller into the default context. 
      
      $${domain} is the default domain set from vars.xml but you can set it
      to any domain you have setup in your user directory.

      -->
      <action application="info"/>
      <action application="python" data="python_example"/>
      <!--<action application="set" data="domain_name=$${domain}"/>-->
      <!-- This example maps the DID 5551212 to ring 1000 in the default context -->
      <!--<action application="transfer" data="1000 XML default"/>-->
    </condition>
  </extension>
</include>

 

참조 : https://wiki.freeswitch.org/wiki/Mod_python#Sample_Python_Scripts