mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-08-13 01:26:58 +00:00
more clean up
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14001 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
22
scripts/python/freepy/INSTALL
Normal file
22
scripts/python/freepy/INSTALL
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
DEPENDENCIES
|
||||
============
|
||||
|
||||
The State Machine Compiler is needed for the freepy connection library to function.
|
||||
|
||||
* wget http://ufpr.dl.sourceforge.net/sourceforge/smc/smc_5_0_0.tgz
|
||||
* tar xvfz smc_5_0_0.tgz
|
||||
* cd smc_5_0_0
|
||||
* cd lib
|
||||
* python setup.py install
|
||||
|
||||
PYTHONPATH
|
||||
==========
|
||||
|
||||
- Add /path/to/freeswitch/scripts/socket to your PYTHONPATH
|
||||
|
||||
TESTING
|
||||
=======
|
||||
|
||||
- Copy/Paste the code from test1() in fshelper.py to your own test module
|
||||
- Adapt code as needed and run
|
40
scripts/python/freepy/README
Normal file
40
scripts/python/freepy/README
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
Socket library to interface w/ freeswitch mod_event_socket from Twisted python applications. Used by WikiPBX for all non-http communication w/ Freeswitch.
|
||||
|
||||
Install
|
||||
=======
|
||||
|
||||
See INSTALL
|
||||
|
||||
Using/Examples
|
||||
==============
|
||||
|
||||
The WikiPBX project uses this lib heavily, so looking at that code (wikipbx.org) is a good example until more docs are added.
|
||||
|
||||
|
||||
Debugging
|
||||
=========
|
||||
|
||||
Set FREEPY_DEBUG_ON = True in globals.py
|
||||
|
||||
TODO: pull this from an environment variable or a config file
|
||||
|
||||
Rebulding State Machines
|
||||
========================
|
||||
|
||||
(you only need to do this if you changed an .sm file)
|
||||
|
||||
for each .sm file:
|
||||
|
||||
java -jar /usr/src/smc/bin/Smc.jar -python -g THE.sm
|
||||
|
||||
and loginrequest.sm -> loginrequest_sm.py with the state machine.
|
||||
|
||||
TODO: create a Makefile to do this stuff
|
||||
TODO: use Miro Samek's HSM framework instead of SMC
|
||||
|
||||
|
||||
|
298
scripts/python/freepy/__init__.py
Normal file
298
scripts/python/freepy/__init__.py
Normal file
@@ -0,0 +1,298 @@
|
||||
"""
|
||||
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
|
||||
Version: MPL 1.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the
|
||||
License.
|
||||
|
||||
The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Anthony Minessale II <anthmct@yahoo.com>
|
||||
Portions created by the Initial Developer are Copyright (C)
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s): Traun Leyden <tleyden@branchcut.com>
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.protocols.basic import LineReceiver
|
||||
from twisted.internet.protocol import Protocol, ClientFactory
|
||||
from twisted.python import failure
|
||||
import time, re
|
||||
from time import strftime
|
||||
from Queue import Queue
|
||||
from freepy import request
|
||||
import freepy.globals
|
||||
from freepy.globals import debug
|
||||
|
||||
"""
|
||||
freepy library -- connect to freeswitch mod_socket_event via python/twisted
|
||||
|
||||
All commands currently use api instead of bgapi. For the networking model
|
||||
used (twisted), this seems to work well and is simpler.
|
||||
|
||||
"""
|
||||
|
||||
DEBUG_ON = "see globals.py to turn on debugging"
|
||||
|
||||
class FreepyDispatcher(LineReceiver):
|
||||
|
||||
def __init__(self, conncb, discocb=None):
|
||||
self.delimiter='\n' # parent class uses this
|
||||
self.conncb=conncb
|
||||
self.discocb=discocb
|
||||
self.requestq = Queue() # queue of pending requests
|
||||
self.active_request = None # the current active (de-queued) request
|
||||
|
||||
def connectionMade(self):
|
||||
debug("FREEPY: Connection made")
|
||||
self.conncb(self)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
if self.discocb:
|
||||
self.discocb(reason)
|
||||
debug("connectionLost: %s" % reason)
|
||||
|
||||
def login(self, passwd):
|
||||
"""
|
||||
send login request
|
||||
"""
|
||||
msg = "auth %s" % passwd
|
||||
req = request.LoginRequest()
|
||||
self.requestq.put(req)
|
||||
self.transport.write("%s\n\n" % msg)
|
||||
debug(">> %s" % msg)
|
||||
return req.getDeferred()
|
||||
|
||||
def _sendCommand(self, command, bgapi):
|
||||
"""
|
||||
there is a lot of duplication in this object, and as many
|
||||
methods as possible should be changed to use this method
|
||||
rather than repeating the code
|
||||
"""
|
||||
command = ("bgapi %s" if bgapi else "api %s") % command
|
||||
req = (request.BgDialoutRequest if bgapi else
|
||||
request.DialoutRequest)()
|
||||
self.requestq.put(req)
|
||||
self.transport.write("%s\n\n" % command)
|
||||
debug(">> %s" % command)
|
||||
return req.getDeferred()
|
||||
|
||||
def confdialout(self, conf_name, sofia_url, bgapi=True):
|
||||
"""
|
||||
Instruct conference to join a particular user via dialout
|
||||
@param conf_name - the name of the conference (arbitrary)
|
||||
@param party2dial - a freeswitch sofia url, eg, sofia/mydomain.com/foo@bar.com
|
||||
@return - a deferred that will be called back with a string like:
|
||||
Reply-Text: +OK Job-UUID: 4d410a8e-2409-11dc-99bf-a5e17fab9c65
|
||||
|
||||
|
||||
"""
|
||||
if bgapi == True:
|
||||
msg = "bgapi conference %s dial %s" % (conf_name,
|
||||
sofia_url)
|
||||
req = request.BgDialoutRequest()
|
||||
else:
|
||||
msg = "api conference %s dial %s" % (conf_name,
|
||||
sofia_url)
|
||||
req = request.DialoutRequest()
|
||||
self.requestq.put(req)
|
||||
self.transport.write("%s\n\n" % msg)
|
||||
debug(">> %s" % msg)
|
||||
return req.getDeferred()
|
||||
|
||||
def originate(self, party2dial, dest_ext_app, bgapi=True):
|
||||
|
||||
if bgapi == True:
|
||||
msg = "bgapi originate %s %s" % (party2dial,
|
||||
dest_ext_app)
|
||||
req = request.BgDialoutRequest()
|
||||
else:
|
||||
msg = "api originate %s %s" % (party2dial,
|
||||
dest_ext_app)
|
||||
req = request.DialoutRequest()
|
||||
self.requestq.put(req)
|
||||
self.transport.write("%s\n\n" % msg)
|
||||
debug(">> %s" % msg)
|
||||
return req.getDeferred()
|
||||
|
||||
def listconf(self, conf_name):
|
||||
"""
|
||||
List users in a conf
|
||||
@param conf_name - the name of the conference (arbitrary)
|
||||
@return - a deferred that will be called back with an array
|
||||
of models.ConfMember instances
|
||||
"""
|
||||
msg = "api conference %s list" % (conf_name)
|
||||
req = request.ListConfRequest()
|
||||
self.requestq.put(req)
|
||||
self.transport.write("%s\n\n" % msg)
|
||||
debug(">> %s" % msg)
|
||||
return req.getDeferred()
|
||||
|
||||
|
||||
def confkick(self, member_id, conf_name, bgapi=False):
|
||||
"""
|
||||
Kick member_id from conf
|
||||
conf_name - name of conf
|
||||
member_id - member id of user to kick, eg, "7"
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
if bgapi == True:
|
||||
msg = "bgapi conference %s kick %s" % (conf_name, member_id)
|
||||
req = request.BgConfKickRequest()
|
||||
else:
|
||||
msg = "api conference %s kick %s" % (conf_name, member_id)
|
||||
req = request.ConfKickRequest()
|
||||
self.requestq.put(req)
|
||||
self.transport.write("%s\n\n" % msg)
|
||||
debug(">> %s" % msg)
|
||||
return req.getDeferred()
|
||||
|
||||
def confdtmf(self, member_id, conf_name, dtmf, bgapi=False):
|
||||
"""
|
||||
Send dtmf to member_id or to all members
|
||||
conf_name - name of conf
|
||||
member_id - member id of user to kick, eg, "7"
|
||||
dtmf - a single dtmf or a string of dtms, eg "1" or "123"
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
msg = "conference %s dtmf %s %s" % \
|
||||
(conf_name, member_id, dtmf)
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
def confsay(self, conf_name, text2speak, bgapi=False):
|
||||
"""
|
||||
Speak text all members
|
||||
conf_name - name of conf
|
||||
dtmf - text to speak
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
msg = "conference %s say %s" % (conf_name, text2speak)
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
def confplay(self, conf_name, snd_url, bgapi=False):
|
||||
"""
|
||||
Play a file to all members
|
||||
conf_name - name of conf
|
||||
dtmf - text to speak
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
msg = "conference %s play %s" % (conf_name, snd_url)
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
def confstop(self, conf_name, bgapi=False):
|
||||
"""
|
||||
Stop playback of all sound files
|
||||
conf_name - name of conf
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
msg = "conference %s stop" % (conf_name)
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
def showchannels(self, bgapi=False):
|
||||
"""
|
||||
Get a list of all live channels on switch
|
||||
|
||||
returns - a deferred that will be called back with a result
|
||||
|
||||
<result row_count="2">
|
||||
<row row_id="1">
|
||||
<uuid>21524b8c-6d19-11dc-9380-357de4a7a612</uuid>
|
||||
<created>2007-09-27 11:46:01</created>
|
||||
<name>sofia/test/4761</name>
|
||||
<state>CS_LOOPBACK</state>
|
||||
<cid_name>FreeSWITCH</cid_name>
|
||||
<cid_num>0000000000</cid_num>
|
||||
<ip_addr></ip_addr>
|
||||
<dest>outgoing2endpoint-6207463</dest>
|
||||
<application>echo</application>
|
||||
<application_data></application_data>
|
||||
<read_codec>PCMU</read_codec>
|
||||
<read_rate>8000</read_rate>
|
||||
<write_codec>PCMU</write_codec>
|
||||
<write_rate>8000</write_rate>
|
||||
</row>
|
||||
...
|
||||
</result>
|
||||
"""
|
||||
msg = "show channels as xml"
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
def sofia_status_profile(self, profile_name, bgapi=False):
|
||||
msg = "sofia status profile %s as xml" % (profile_name)
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
def sofia_profile_restart(self, sofia_profile_name, bgapi = False):
|
||||
|
||||
msg = "sofia profile %s restart" % (sofia_profile_name)
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
def killchan(self, uuid, bgapi = False):
|
||||
return self._sendCommand("uuid_kill %s" % (uuid), bgapi)
|
||||
|
||||
def broadcast(self, uuid, path, legs, bgapi = False):
|
||||
msg = "uuid_broadcast %s %s %s" % (uuid, path, legs)
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
def transfer(self, uuid, dest_ext, legs, bgapi = False):
|
||||
"""
|
||||
transfer <uuid> [-bleg|-both] <dest-exten>
|
||||
"""
|
||||
msg = "uuid_transfer %s %s %s" % (uuid, legs, dest_ext)
|
||||
return self._sendCommand(msg, bgapi)
|
||||
|
||||
|
||||
def lineReceived(self, line):
|
||||
debug("<< %s" % line)
|
||||
if not self.active_request:
|
||||
|
||||
# if no active request pending, we ignore
|
||||
# blank lines
|
||||
if not line.strip():
|
||||
return
|
||||
|
||||
# if no active request, dequeue a new one
|
||||
if self.requestq.empty():
|
||||
# we are receiving non-empty data from fs without an
|
||||
# active request pending. that means that
|
||||
# there is a bug in the protocol handler
|
||||
# (or possibly in fs)
|
||||
raise Exception("Received line: %s w/ no pending requests" % line)
|
||||
self.active_request = self.requestq.get()
|
||||
|
||||
# tell the request to process the line, and tell us
|
||||
# if its finished or not. if its finished, we remove it
|
||||
# as the active request so that a new active request will
|
||||
# be de-queued.
|
||||
finished = self.active_request.process(line)
|
||||
if finished == True:
|
||||
self.active_request = None
|
||||
|
96
scripts/python/freepy/apirequest.sm
Normal file
96
scripts/python/freepy/apirequest.sm
Normal file
@@ -0,0 +1,96 @@
|
||||
%start MainMap::Startup
|
||||
%class ApiRequest
|
||||
|
||||
%map MainMap
|
||||
%%
|
||||
|
||||
Startup
|
||||
{
|
||||
ApiResponse
|
||||
ApiResponseStarted
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
ApiResponseStarted
|
||||
{
|
||||
ContentLength
|
||||
ContentPreStarted
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ContentPreStarted
|
||||
{
|
||||
BlankLine
|
||||
ContentStarted
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ContentStarted
|
||||
{
|
||||
ProcessLine(line)
|
||||
[ctxt.add_content(line) == True]
|
||||
Startup
|
||||
{
|
||||
setRequestFinished(); callbackDeferred(ctxt.getResponse());
|
||||
}
|
||||
|
||||
ProcessLine(line)
|
||||
nil
|
||||
{
|
||||
// for some reason, have to add doNothing() here or
|
||||
// importing smc will fail. looks like smc bug.
|
||||
doNothing();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Default
|
||||
{
|
||||
BlankLine
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting blank line");
|
||||
}
|
||||
|
||||
ContentFinished
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting content to be finished");
|
||||
}
|
||||
|
||||
ContentLength
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting content-length header");
|
||||
}
|
||||
|
||||
ApiResponse
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting api response");
|
||||
}
|
||||
|
||||
ProcessLine(line)
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting needing to process a line");
|
||||
}
|
||||
}
|
||||
|
||||
%%
|
208
scripts/python/freepy/apirequest_sm.py
Normal file
208
scripts/python/freepy/apirequest_sm.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# DO NOT EDIT.
|
||||
# generated by smc (http://smc.sourceforge.net/)
|
||||
# from file : apirequest.sm
|
||||
|
||||
import statemap
|
||||
|
||||
|
||||
class ApiRequestState(statemap.State):
|
||||
|
||||
def Entry(self, fsm):
|
||||
pass
|
||||
|
||||
def Exit(self, fsm):
|
||||
pass
|
||||
|
||||
def ApiResponse(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def ContentFinished(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def ContentLength(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def ProcessLine(self, fsm, line):
|
||||
self.Default(fsm)
|
||||
|
||||
def Default(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write('TRANSITION : Default\n')
|
||||
msg = "\n\tState: %s\n\tTransition: %s" % (
|
||||
fsm.getState().getName(), fsm.getTransition())
|
||||
raise statemap.TransitionUndefinedException, msg
|
||||
|
||||
class MainMap_Default(ApiRequestState):
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.BlankLine()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting blank line")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def ContentFinished(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ContentFinished()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting content to be finished")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def ContentLength(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ContentLength()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting content-length header")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def ApiResponse(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ApiResponse()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting api response")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def ProcessLine(self, fsm, line):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ProcessLine(line)\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting needing to process a line")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
class MainMap_Startup(MainMap_Default):
|
||||
|
||||
def ApiResponse(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Startup.ApiResponse()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.ApiResponseStarted)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_ApiResponseStarted(MainMap_Default):
|
||||
|
||||
def ContentLength(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.ApiResponseStarted.ContentLength()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.ContentPreStarted)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_ContentPreStarted(MainMap_Default):
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.ContentPreStarted.BlankLine()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.ContentStarted)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_ContentStarted(MainMap_Default):
|
||||
|
||||
def ProcessLine(self, fsm, line):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.ContentStarted.ProcessLine(line)\n")
|
||||
|
||||
if ctxt.add_content(line) == True :
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.callbackDeferred(ctxt.getResponse())
|
||||
finally:
|
||||
fsm.setState(MainMap.Startup)
|
||||
fsm.getState().Entry(fsm)
|
||||
else:
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.doNothing()
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
|
||||
class MainMap:
|
||||
|
||||
Startup = MainMap_Startup('MainMap.Startup', 0)
|
||||
ApiResponseStarted = MainMap_ApiResponseStarted('MainMap.ApiResponseStarted', 1)
|
||||
ContentPreStarted = MainMap_ContentPreStarted('MainMap.ContentPreStarted', 2)
|
||||
ContentStarted = MainMap_ContentStarted('MainMap.ContentStarted', 3)
|
||||
Default = MainMap_Default('MainMap.Default', -1)
|
||||
|
||||
class ApiRequest_sm(statemap.FSMContext):
|
||||
|
||||
def __init__(self, owner):
|
||||
statemap.FSMContext.__init__(self)
|
||||
self._owner = owner
|
||||
self.setState(MainMap.Startup)
|
||||
MainMap.Startup.Entry(self)
|
||||
|
||||
def ApiResponse(self):
|
||||
self._transition = 'ApiResponse'
|
||||
self.getState().ApiResponse(self)
|
||||
self._transition = None
|
||||
|
||||
def BlankLine(self):
|
||||
self._transition = 'BlankLine'
|
||||
self.getState().BlankLine(self)
|
||||
self._transition = None
|
||||
|
||||
def ContentFinished(self):
|
||||
self._transition = 'ContentFinished'
|
||||
self.getState().ContentFinished(self)
|
||||
self._transition = None
|
||||
|
||||
def ContentLength(self):
|
||||
self._transition = 'ContentLength'
|
||||
self.getState().ContentLength(self)
|
||||
self._transition = None
|
||||
|
||||
def ProcessLine(self, *arglist):
|
||||
self._transition = 'ProcessLine'
|
||||
self.getState().ProcessLine(self, *arglist)
|
||||
self._transition = None
|
||||
|
||||
def getState(self):
|
||||
if self._state == None:
|
||||
raise statemap.StateUndefinedException
|
||||
return self._state
|
||||
|
||||
def getOwner(self):
|
||||
return self._owner
|
||||
|
76
scripts/python/freepy/bgapirequest.sm
Normal file
76
scripts/python/freepy/bgapirequest.sm
Normal file
@@ -0,0 +1,76 @@
|
||||
%start MainMap::Startup
|
||||
%class BgApiRequest
|
||||
|
||||
%map MainMap
|
||||
%%
|
||||
|
||||
Startup
|
||||
{
|
||||
CommandReply
|
||||
ApiResponseStarted
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
ApiResponseStarted
|
||||
{
|
||||
ReplyText
|
||||
GotReplyText
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
GotReplyText
|
||||
{
|
||||
BlankLine
|
||||
nil
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JobUuid
|
||||
Startup
|
||||
{
|
||||
setRequestFinished(); callOrErrback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Default
|
||||
{
|
||||
BlankLine
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting blank line"); }
|
||||
|
||||
CommandReply
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting command reply");
|
||||
}
|
||||
|
||||
ReplyText
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting reply text");
|
||||
}
|
||||
|
||||
ProcessLine(line)
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure handling bgapi response - was not expecting line needing to be processed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%%
|
180
scripts/python/freepy/bgapirequest_sm.py
Normal file
180
scripts/python/freepy/bgapirequest_sm.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# DO NOT EDIT.
|
||||
# generated by smc (http://smc.sourceforge.net/)
|
||||
# from file : bgapirequest.sm
|
||||
|
||||
import statemap
|
||||
|
||||
|
||||
class BgApiRequestState(statemap.State):
|
||||
|
||||
def Entry(self, fsm):
|
||||
pass
|
||||
|
||||
def Exit(self, fsm):
|
||||
pass
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def CommandReply(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def JobUuid(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def ProcessLine(self, fsm, line):
|
||||
self.Default(fsm)
|
||||
|
||||
def ReplyText(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def Default(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write('TRANSITION : Default\n')
|
||||
msg = "\n\tState: %s\n\tTransition: %s" % (
|
||||
fsm.getState().getName(), fsm.getTransition())
|
||||
raise statemap.TransitionUndefinedException, msg
|
||||
|
||||
class MainMap_Default(BgApiRequestState):
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.BlankLine()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting blank line")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def CommandReply(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.CommandReply()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting command reply")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def ReplyText(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ReplyText()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting reply text")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def ProcessLine(self, fsm, line):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ProcessLine(line)\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure handling bgapi response - was not expecting line needing to be processed")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
class MainMap_Startup(MainMap_Default):
|
||||
|
||||
def CommandReply(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Startup.CommandReply()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.ApiResponseStarted)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_ApiResponseStarted(MainMap_Default):
|
||||
|
||||
def ReplyText(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.ApiResponseStarted.ReplyText()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.GotReplyText)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_GotReplyText(MainMap_Default):
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.GotReplyText.BlankLine()\n")
|
||||
|
||||
|
||||
def JobUuid(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.GotReplyText.JobUuid()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.callOrErrback()
|
||||
finally:
|
||||
fsm.setState(MainMap.Startup)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap:
|
||||
|
||||
Startup = MainMap_Startup('MainMap.Startup', 0)
|
||||
ApiResponseStarted = MainMap_ApiResponseStarted('MainMap.ApiResponseStarted', 1)
|
||||
GotReplyText = MainMap_GotReplyText('MainMap.GotReplyText', 2)
|
||||
Default = MainMap_Default('MainMap.Default', -1)
|
||||
|
||||
class BgApiRequest_sm(statemap.FSMContext):
|
||||
|
||||
def __init__(self, owner):
|
||||
statemap.FSMContext.__init__(self)
|
||||
self._owner = owner
|
||||
self.setState(MainMap.Startup)
|
||||
MainMap.Startup.Entry(self)
|
||||
|
||||
def BlankLine(self):
|
||||
self._transition = 'BlankLine'
|
||||
self.getState().BlankLine(self)
|
||||
self._transition = None
|
||||
|
||||
def CommandReply(self):
|
||||
self._transition = 'CommandReply'
|
||||
self.getState().CommandReply(self)
|
||||
self._transition = None
|
||||
|
||||
def JobUuid(self):
|
||||
self._transition = 'JobUuid'
|
||||
self.getState().JobUuid(self)
|
||||
self._transition = None
|
||||
|
||||
def ProcessLine(self, *arglist):
|
||||
self._transition = 'ProcessLine'
|
||||
self.getState().ProcessLine(self, *arglist)
|
||||
self._transition = None
|
||||
|
||||
def ReplyText(self):
|
||||
self._transition = 'ReplyText'
|
||||
self.getState().ReplyText(self)
|
||||
self._transition = None
|
||||
|
||||
def getState(self):
|
||||
if self._state == None:
|
||||
raise statemap.StateUndefinedException
|
||||
return self._state
|
||||
|
||||
def getOwner(self):
|
||||
return self._owner
|
||||
|
287
scripts/python/freepy/fseventlistener.py
Normal file
287
scripts/python/freepy/fseventlistener.py
Normal file
@@ -0,0 +1,287 @@
|
||||
"""
|
||||
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
|
||||
Version: MPL 1.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the
|
||||
License.
|
||||
|
||||
The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Anthony Minessale II <anthmct@yahoo.com>
|
||||
Portions created by the Initial Developer are Copyright (C)
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s): Traun Leyden <tleyden@branchcut.com>
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.protocols.basic import LineReceiver
|
||||
from twisted.internet.protocol import Protocol, ClientFactory
|
||||
from twisted.python import failure
|
||||
import time, re
|
||||
from time import strftime
|
||||
from Queue import Queue
|
||||
from freepy import request
|
||||
|
||||
"""
|
||||
This class connects to freeswitch and listens for
|
||||
events and calls callback with the events.
|
||||
|
||||
Example messages
|
||||
=================
|
||||
|
||||
Content-Length: 675
|
||||
Content-Type: text/event-xml
|
||||
|
||||
<event>
|
||||
<header name="force-contact" value="nat-connectile-dysfunction"></header>
|
||||
etc..
|
||||
</event>
|
||||
Content-Length: 875
|
||||
Content-Type: text/event-xml
|
||||
|
||||
<event>
|
||||
...
|
||||
</event>
|
||||
|
||||
"""
|
||||
|
||||
class FreeswitchEventListener(LineReceiver):
|
||||
|
||||
def __init__(self, conncb, discocb=None):
|
||||
self.delimiter='\n' # parent class uses this
|
||||
self.conncb=conncb
|
||||
self.discocb=discocb
|
||||
self.bufferlines = []
|
||||
self.receiving_event = False # state to track if in <event>..</event>
|
||||
self.requestq = Queue() # queue of pending requests
|
||||
self.active_request = None # the current active (de-queued) request
|
||||
|
||||
def connectionMade(self):
|
||||
self.conncb(self)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
if self.discocb:
|
||||
self.discocb(reason)
|
||||
print "connectionLost: %s" % reason
|
||||
|
||||
|
||||
def login(self, passwd):
|
||||
"""
|
||||
send login request
|
||||
"""
|
||||
msg = "auth %s" % passwd
|
||||
req = request.LoginRequest()
|
||||
self.active_request = req
|
||||
self.transport.write("%s\n\n" % str(msg))
|
||||
return req.getDeferred()
|
||||
|
||||
def sniff_events(self, output_type, events):
|
||||
"""
|
||||
@param output_type - eg, xml or plain
|
||||
@param events - list of events, eg ['all']
|
||||
"""
|
||||
event_list = " ".join(events)
|
||||
msg = "event %s %s" % (output_type, event_list)
|
||||
self.transport.write("%s\n\n" % str(msg))
|
||||
|
||||
def sniff_custom_events(self, output_type, events):
|
||||
"""
|
||||
when sniffing custom events, the CUSTOM keyword
|
||||
must be present in message
|
||||
http://wiki.freeswitch.org/wiki/Event_Socket#event
|
||||
|
||||
@param output_type - eg, xml or plain
|
||||
@param events - list of events, eg ['all']
|
||||
"""
|
||||
event_list = " ".join(events)
|
||||
msg = "event %s CUSTOM %s" % (output_type, event_list)
|
||||
self.transport.write("%s\n\n" % msg)
|
||||
|
||||
def sniff_all_events(self, output_type):
|
||||
"""
|
||||
@param output_type - eg, xml or plain
|
||||
"""
|
||||
msg = "event %s all" % output_type
|
||||
self.transport.write("%s\n\n" % str(msg))
|
||||
|
||||
def lineReceived(self, line):
|
||||
if not self.active_request:
|
||||
if line.find("<event>") != -1:
|
||||
self.receiving_event = True
|
||||
if self.receiving_event:
|
||||
self.bufferlines.append(line)
|
||||
if line.find("</event>") != -1:
|
||||
event_xml_str = "\n".join(self.bufferlines)
|
||||
self.eventReceived(event_xml_str)
|
||||
self.bufferlines = []
|
||||
self.receiving_event = False
|
||||
else:
|
||||
# we have an active request (seperate state machine)
|
||||
# tell the request to process the line, and tell us
|
||||
# if its finished or not. if its finished, we remove it
|
||||
# as the active request so that a new active request will
|
||||
# be de-queued.
|
||||
finished = self.active_request.process(line)
|
||||
if finished == True:
|
||||
self.active_request = None
|
||||
|
||||
def eventReceived(self, event_xml_str):
|
||||
"""
|
||||
should be overridden by subclasses.
|
||||
"""
|
||||
raise Exception("This is an abstract class, should be overridden "
|
||||
"in a subclass")
|
||||
|
||||
class FreeswitchEventListenerFactory(ClientFactory):
|
||||
|
||||
def __init__(self, protoclass, host=None, passwd=None, port=None):
|
||||
"""
|
||||
@param protoclass - a class (not instance) of the protocol
|
||||
should be a subclass of a FreeswitchEventListener
|
||||
"""
|
||||
|
||||
# dictionary of observers. key: event name, value: list of observers
|
||||
self.event2observer = {}
|
||||
|
||||
self.protoclass=protoclass
|
||||
|
||||
if host:
|
||||
self.host = host
|
||||
if passwd:
|
||||
self.passwd = passwd
|
||||
if port:
|
||||
self.port = port
|
||||
|
||||
self.protocol = None
|
||||
self.connection_deferred = None
|
||||
self.num_attempts = 0
|
||||
|
||||
def addobserver(self, event_name, observer):
|
||||
"""
|
||||
@param event_name, eg "CHANNEL_ANSWER"
|
||||
@param observer (instance of object that has an eventReceived() method
|
||||
"""
|
||||
observers = self.event2observer.get(event_name, [])
|
||||
observers.append(observer)
|
||||
self.event2observer[event_name] = observers
|
||||
|
||||
def dispatch2observers(self, event_name, event_xml_str, event_dom):
|
||||
"""
|
||||
called back by the underlying protocol upon receiving an
|
||||
event from freeswitch. Currently subclasses must explicitly
|
||||
call this method from their eventReceived method for observers
|
||||
to get the message. TODO: move this call to FreeswitchEventListener
|
||||
and use observer pattern instead of any subclassing.
|
||||
"""
|
||||
observers = self.event2observer.get(event_name, [])
|
||||
for observer in observers:
|
||||
observer.eventReceived(event_name, event_xml_str, event_dom)
|
||||
|
||||
def reset(self):
|
||||
self.protocol = None
|
||||
self.connection_deferred = None
|
||||
|
||||
def connect(self):
|
||||
|
||||
if self.protocol:
|
||||
# if we have a protocol object, we are connected (since we always
|
||||
# null it upon any disconnection)
|
||||
return defer.succeed(self.protocol)
|
||||
|
||||
#if self.connection_deferred:
|
||||
# we are already connecting, return existing dfrd
|
||||
# return self.connection_deferred
|
||||
|
||||
# connect and automatically login after connection
|
||||
if not self.connection_deferred:
|
||||
self.connection_deferred = defer.Deferred()
|
||||
self.connection_deferred.addCallback(self.dologin)
|
||||
self.connection_deferred.addErrback(self.generalError)
|
||||
reactor.connectTCP(self.host, self.port, self)
|
||||
return self.connection_deferred
|
||||
|
||||
def conncb(self, protocol):
|
||||
self.protocol = protocol
|
||||
self.protocol.__dict__["factory"] = self
|
||||
deferred2callback = self.connection_deferred
|
||||
self.connection_deferred = None
|
||||
deferred2callback.callback(self.protocol)
|
||||
|
||||
|
||||
def generalError(self, failure):
|
||||
print "General error: %s" % failure
|
||||
return failure
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
pass
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
return self.protoclass(self.conncb, self.discocb)
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
print "clientConnectionLost! conn=%s, reason=%s" % (connector,
|
||||
reason)
|
||||
self.connection_deferred = None
|
||||
self.protocol = None
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
|
||||
if self.num_attempts < 10000:
|
||||
self.num_attempts += 1
|
||||
print "Connection to %s:%s refused, retrying attempt #%s in " \
|
||||
"5 seconds" % (self.host, self.port, self.num_attempts)
|
||||
return reactor.callLater(5, self.connect)
|
||||
else:
|
||||
print "clientConnectionFailed! conn=%s, reason=%s" % (connector,
|
||||
reason)
|
||||
print ("Retry attempts exhausted, total attempts: %s" %
|
||||
self.num_attempts)
|
||||
deferred2callback = self.connection_deferred
|
||||
deferred2callback.errback(reason)
|
||||
|
||||
def discocb(self, reason):
|
||||
print "disconnected. reason: %s" % reason
|
||||
self.protocol = None
|
||||
|
||||
def dologin(self, connectmsg):
|
||||
return self.protocol.login(self.passwd)
|
||||
|
||||
|
||||
def test1():
|
||||
fel = FreeswitchEventListener
|
||||
factory = FreeswitchEventListenerFactory(protoclass=fel,
|
||||
host="127.0.0.1",
|
||||
port=8021,
|
||||
passwd="ClueCon")
|
||||
|
||||
def connected(result):
|
||||
print "We connected, result: %s" % result
|
||||
events=['sofia::register','sofia::expire']
|
||||
factory.protocol.sniff_custom_events(output_type="xml", events=events)
|
||||
#factory.protocol.sniff_all_events(output_type="xml")
|
||||
|
||||
def failure(failure):
|
||||
print "Failed to connect: %s" % failure
|
||||
|
||||
d = factory.connect()
|
||||
d.addCallbacks(connected, failure)
|
||||
d.addErrback(failure)
|
||||
|
||||
reactor.run()
|
||||
|
||||
if __name__=="__main__":
|
||||
test1()
|
||||
|
532
scripts/python/freepy/fshelper.py
Normal file
532
scripts/python/freepy/fshelper.py
Normal file
@@ -0,0 +1,532 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
|
||||
Version: MPL 1.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the
|
||||
License.
|
||||
|
||||
The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Anthony Minessale II <anthmct@yahoo.com>
|
||||
Portions created by the Initial Developer are Copyright (C)
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s): Traun Leyden <tleyden@branchcut.com>
|
||||
"""
|
||||
|
||||
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.internet.protocol import ClientFactory
|
||||
import freepy
|
||||
|
||||
class FsHelper(ClientFactory):
|
||||
|
||||
def __init__(self, host=None, passwd=None, port=None):
|
||||
if host:
|
||||
self.host = host
|
||||
if passwd:
|
||||
self.passwd = passwd
|
||||
if port:
|
||||
self.port = port
|
||||
|
||||
self.freepyd = None
|
||||
self.connection_deferred = None
|
||||
|
||||
def reset(self):
|
||||
self.freepyd = None
|
||||
self.connection_deferred = None
|
||||
|
||||
def connect(self):
|
||||
|
||||
if self.freepyd:
|
||||
# if we have a protocol object, we are connected (since we always
|
||||
# null it upon any disconnection)
|
||||
return defer.succeed("Connected")
|
||||
|
||||
if self.connection_deferred:
|
||||
# we are already connecting, return existing dfrd
|
||||
return self.connection_deferred
|
||||
|
||||
self.connection_deferred = defer.Deferred()
|
||||
self.connection_deferred.addCallback(self.dologin)
|
||||
self.connection_deferred.addErrback(self.generalError)
|
||||
print "freepy connecting to %s:%s" % (self.host, self.port)
|
||||
reactor.connectTCP(self.host, self.port, self)
|
||||
return self.connection_deferred
|
||||
|
||||
def conncb(self, freepyd):
|
||||
self.freepyd = freepyd
|
||||
deferred2callback = self.connection_deferred
|
||||
self.connection_deferred = None
|
||||
deferred2callback.callback("Connected")
|
||||
|
||||
|
||||
def generalError(self, failure):
|
||||
print "General error: %s" % failure
|
||||
return failure
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
pass
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
return freepy.FreepyDispatcher(self.conncb, self.discocb)
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
print "clientConnectionLost! conn=%s, reason=%s" % (connector,
|
||||
reason)
|
||||
self.connection_deferred = None
|
||||
self.freepyd = None
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
print "clientConnectionFailed! conn=%s, reason=%s" % (connector,
|
||||
reason)
|
||||
self.freepyd = None
|
||||
deferred2callback = self.connection_deferred
|
||||
self.connection_deferred = None
|
||||
deferred2callback.errback(reason)
|
||||
|
||||
def discocb(self, reason):
|
||||
print "disconnected. reason: %s" % reason
|
||||
self.freepyd = None
|
||||
|
||||
def dologin(self, connectmsg):
|
||||
return self.freepyd.login(self.passwd)
|
||||
|
||||
def originate(self, party2dial, dest_ext_app, bgapi=True):
|
||||
"""
|
||||
party2dial - the first argument to the originate command,
|
||||
eg, sofia/profile_name/1234@domain.com
|
||||
dest_ext_app - the second argument to the originate command,
|
||||
eg, &park() or 4761
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
([(True, 'Reply-Text: +OK Job-UUID: d07ad7de-2406-11dc-aea3-e3b2e56b7a2c')],)
|
||||
|
||||
"""
|
||||
|
||||
def originate_inner(ignored):
|
||||
deferreds = []
|
||||
deferred = self.freepyd.originate(party2dial,
|
||||
dest_ext_app,
|
||||
bgapi)
|
||||
return deferred
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(originate_inner)
|
||||
return d
|
||||
|
||||
|
||||
|
||||
|
||||
def dialconf(self, people2dial, conf_name, bgapi=True):
|
||||
"""
|
||||
conf_name - name of conf TODO: change to match db
|
||||
people2dial - an array of dictionaries:
|
||||
'name': name
|
||||
'number': number
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
([(True, 'Reply-Text: +OK Job-UUID: d07ad7de-2406-11dc-aea3-e3b2e56b7a2c')],)
|
||||
|
||||
Its a bit ugly because its a deferred list callback.
|
||||
|
||||
"""
|
||||
|
||||
def dialconf_inner(ignored):
|
||||
deferreds = []
|
||||
for person2dial in people2dial:
|
||||
sofia_url = person2dial['number']
|
||||
deferred = self.freepyd.confdialout(conf_name,
|
||||
sofia_url,
|
||||
bgapi)
|
||||
deferreds.append(deferred)
|
||||
return defer.DeferredList(deferreds)
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(dialconf_inner)
|
||||
return d
|
||||
|
||||
def listconf(self, conf_name):
|
||||
"""
|
||||
conf_name - name of conf
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
def listconf_inner(ignored):
|
||||
deferred = self.freepyd.listconf(conf_name)
|
||||
return deferred
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(listconf_inner)
|
||||
return d
|
||||
|
||||
def confkick(self, member_id, conf_name, bgapi=True):
|
||||
"""
|
||||
conf_name - name of conf
|
||||
member_id - member id of user to kick, eg, "7"
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
def confkick_inner(ignored):
|
||||
#if type(member_id) == type(""):
|
||||
# member_id = int(member_id)
|
||||
deferred = self.freepyd.confkick(member_id, conf_name, bgapi)
|
||||
return deferred
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(confkick_inner)
|
||||
return d
|
||||
|
||||
def confdtmf(self, member_id, conf_name, dtmf, bgapi=True):
|
||||
"""
|
||||
Send dtmf(s) to a conference
|
||||
conf_name - name of conf
|
||||
member_id - member id of user to kick, eg, "7", or "all"
|
||||
dtmf - a single dtmf or a string of dtms, eg "1" or "123"
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
def confdtmf_inner(ignored):
|
||||
print "confdtmf_inner called"
|
||||
deferred = self.freepyd.confdtmf(member_id, conf_name, dtmf, bgapi)
|
||||
return deferred
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(confdtmf_inner)
|
||||
return d
|
||||
|
||||
|
||||
def confsay(self, conf_name, text2speak, bgapi=True):
|
||||
"""
|
||||
conf_name - name of conf
|
||||
text2speak - text to speak
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
def confsay_inner(ignored):
|
||||
deferred = self.freepyd.confsay(conf_name, text2speak, bgapi)
|
||||
return deferred
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(confsay_inner)
|
||||
return d
|
||||
|
||||
def confplay(self, conf_name, snd_url, bgapi=True):
|
||||
"""
|
||||
conf_name - name of conf
|
||||
snd_url - url to sound file
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
def confplay_inner(ignored):
|
||||
deferred = self.freepyd.confplay(conf_name, snd_url, bgapi)
|
||||
return deferred
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(confplay_inner)
|
||||
return d
|
||||
|
||||
def confstop(self, conf_name, bgapi=True):
|
||||
"""
|
||||
stop playback of all sounds
|
||||
|
||||
conf_name - name of conf
|
||||
returns - a deferred that will be called back with a result
|
||||
like:
|
||||
|
||||
TODO: add this
|
||||
"""
|
||||
def confstop_inner(ignored):
|
||||
deferred = self.freepyd.confstop(conf_name, bgapi)
|
||||
return deferred
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(confstop_inner)
|
||||
return d
|
||||
|
||||
|
||||
def showchannels(self, bgapi=True):
|
||||
|
||||
def showchannels_inner(ignored):
|
||||
df = self.freepyd.showchannels(bgapi)
|
||||
return df
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(showchannels_inner)
|
||||
return d
|
||||
|
||||
def killchan(self, uuid, bgapi=True):
|
||||
|
||||
def killchan_inner(ignored):
|
||||
df = self.freepyd.killchan(uuid, bgapi)
|
||||
return df
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(killchan_inner)
|
||||
return d
|
||||
|
||||
|
||||
def broadcast(self, uuid, path, legs="both", bgapi=True):
|
||||
"""
|
||||
@legs - one of the following strings: aleg|bleg|both
|
||||
"""
|
||||
def broadcast_inner(ignored):
|
||||
df = self.freepyd.broadcast(uuid, path, legs, bgapi)
|
||||
return df
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(broadcast_inner)
|
||||
return d
|
||||
|
||||
def transfer(self, uuid, dest_ext, legs="-both", bgapi=True):
|
||||
"""
|
||||
@legs -bleg|-both
|
||||
"""
|
||||
def transfer_inner(ignored):
|
||||
df = self.freepyd.transfer(uuid, dest_ext, legs, bgapi)
|
||||
return df
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(transfer_inner)
|
||||
return d
|
||||
|
||||
def sofia_profile_restart(self, profile_name, bgapi=True):
|
||||
|
||||
def sofia_profile_restart_inner(ignored):
|
||||
df = self.freepyd.sofia_profile_restart(profile_name,
|
||||
bgapi)
|
||||
return df
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(sofia_profile_restart_inner)
|
||||
return d
|
||||
|
||||
|
||||
def sofia_status_profile(self, profile_name, bgapi=True):
|
||||
|
||||
def sofia_status_profile_inner(ignored):
|
||||
df = self.freepyd.sofia_status_profile(profile_name,
|
||||
bgapi)
|
||||
return df
|
||||
|
||||
d = self.connect()
|
||||
d.addCallback(sofia_status_profile_inner)
|
||||
return d
|
||||
|
||||
|
||||
class FsHelperTest:
|
||||
def __init__(self, fshelper):
|
||||
self.fshelper=fshelper
|
||||
pass
|
||||
|
||||
def test_dialconf(self):
|
||||
|
||||
# the following parties will be dialed out from the conference
|
||||
# called "freeswitch" on the local freeswitch instance.
|
||||
# one party is actually another conference, just to make
|
||||
# the example more confusing.
|
||||
people2dial = [{'name':'freeswitch',
|
||||
'number':'888@conference.freeswitch.org'},
|
||||
{'name':'mouselike',
|
||||
'number':'904@mouselike.org'}]
|
||||
d = self.fshelper.dialconf(people2dial, "freeswitch", bgapi=False)
|
||||
def failed(error):
|
||||
print "Failed to dial users!"
|
||||
reactor.stop()
|
||||
return error
|
||||
d.addErrback(failed)
|
||||
def worked(*args):
|
||||
print "Worked! Dialed user result: %s" % str(args)
|
||||
d.addCallback(worked)
|
||||
return d
|
||||
|
||||
def test_listconf(self):
|
||||
|
||||
d = self.fshelper.listconf("freeswitch")
|
||||
def failed(failure):
|
||||
print "Failed to list users!"
|
||||
reactor.stop()
|
||||
return failure
|
||||
d.addErrback(failed)
|
||||
def worked(*args):
|
||||
print "List of users in conf: %s" % str(args)
|
||||
return args[0]
|
||||
d.addCallback(worked)
|
||||
return d
|
||||
|
||||
def test_confkick(self, member_id="6", conf_name="freeswitch"):
|
||||
|
||||
d = self.fshelper.confkick(member_id, conf_name)
|
||||
def failed(failure):
|
||||
print "Failed to kick user!"
|
||||
reactor.stop()
|
||||
return failure
|
||||
d.addErrback(failed)
|
||||
def worked(*args):
|
||||
print "Kicked user from conf, result: %s" % str(args)
|
||||
d.addCallback(worked)
|
||||
|
||||
|
||||
def test1():
|
||||
kick_everyone = False
|
||||
fshelper = FsHelper(host="127.0.0.1",
|
||||
passwd="ClueCon",
|
||||
port=8021)
|
||||
fsht = FsHelperTest(fshelper)
|
||||
fsht.test_dialconf()
|
||||
d = fsht.test_listconf()
|
||||
def kickeveryone(members):
|
||||
print "Kickeveryone called w/ %s (type: %s)" % (members,
|
||||
type(members))
|
||||
for member in members:
|
||||
fsht.test_confkick(member.member_id)
|
||||
|
||||
def failed(failure):
|
||||
print "failed: %s" % str(failure)
|
||||
reactor.stop()
|
||||
if kick_everyone:
|
||||
d.addCallback(kickeveryone)
|
||||
d.addErrback(failed)
|
||||
#fsht.test_confkick()
|
||||
#d = fshelper.connect()
|
||||
#def connected(*args):
|
||||
# fsht.test_dialconf()
|
||||
# fsht.test_listconf()
|
||||
#d.addCallback(connected)
|
||||
reactor.run()
|
||||
|
||||
def test2():
|
||||
fshelper = FsHelper(host="127.0.0.1",
|
||||
passwd="ClueCon",
|
||||
port=8021)
|
||||
fshelper.sofia_profile_restart("mydomain.com")
|
||||
reactor.run()
|
||||
|
||||
def test3():
|
||||
fshelper = FsHelper(host="127.0.0.1",
|
||||
passwd="ClueCon",
|
||||
port=8021)
|
||||
print "Calling originate.."
|
||||
party2dial="sofia/foo/600@192.168.1.202:5080"
|
||||
d = fshelper.originate(party2dial=party2dial,
|
||||
dest_ext_app="101",
|
||||
bgapi=True)
|
||||
|
||||
def worked(result):
|
||||
print "Originate succeeded: %s" % result
|
||||
reactor.stop()
|
||||
|
||||
def failed(failure):
|
||||
print "failed: %s" % str(failure)
|
||||
reactor.stop()
|
||||
|
||||
d.addCallback(worked)
|
||||
d.addErrback(failed)
|
||||
reactor.run()
|
||||
|
||||
|
||||
def test4():
|
||||
fshelper = FsHelper(host="127.0.0.1",
|
||||
passwd="ClueCon",
|
||||
port=8021)
|
||||
|
||||
def worked(result):
|
||||
print "Originate succeeded: %s" % result
|
||||
#reactor.stop()
|
||||
|
||||
def failed(failure):
|
||||
print "failed: %s" % str(failure)
|
||||
#reactor.stop()
|
||||
|
||||
|
||||
dest_ext_app = "101"
|
||||
party2dial="sofia/foo/600@192.168.1.202:5080"
|
||||
d = fshelper.originate(party2dial=party2dial,
|
||||
dest_ext_app=dest_ext_app,
|
||||
bgapi=True)
|
||||
d.addCallback(worked)
|
||||
d.addErrback(failed)
|
||||
party2dial="sofia/foo/someone@bar.com"
|
||||
d2 = fshelper.originate(party2dial=party2dial,
|
||||
dest_ext_app=dest_ext_app,
|
||||
bgapi=True)
|
||||
|
||||
d2.addCallback(worked)
|
||||
d2.addErrback(failed)
|
||||
reactor.run()
|
||||
|
||||
|
||||
def test5():
|
||||
fshelper = FsHelper(host="127.0.0.1",
|
||||
passwd="ClueCon",
|
||||
port=8021)
|
||||
|
||||
def worked(result):
|
||||
print "Originate succeeded: %s" % result
|
||||
#reactor.stop()
|
||||
|
||||
def failed(failure):
|
||||
print "failed: %s" % str(failure)
|
||||
#reactor.stop()
|
||||
|
||||
for i in xrange(20):
|
||||
|
||||
party2dial="sofia/foo/600@192.168.1.202:5080"
|
||||
d = fshelper.originate(party2dial=party2dial,
|
||||
dest_ext_app="700",
|
||||
bgapi=True)
|
||||
d.addCallback(worked)
|
||||
d.addErrback(failed)
|
||||
|
||||
reactor.run()
|
||||
|
||||
def test6():
|
||||
"""
|
||||
show channels for a given sofia profile
|
||||
"""
|
||||
fshelper = FsHelper(host="127.0.0.1",
|
||||
passwd="ClueCon",
|
||||
port=8021)
|
||||
from wikipbx import channelsutil
|
||||
def show_chanels(raw_xml):
|
||||
print raw_xml
|
||||
|
||||
def failure(failure):
|
||||
print failure
|
||||
|
||||
d = fshelper.showchannels(bgapi=False)
|
||||
d.addCallback(show_chanels)
|
||||
d.addErrback(failure)
|
||||
reactor.run()
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
#test1()
|
||||
#test2()
|
||||
#test3()
|
||||
test4()
|
||||
#test5()
|
14
scripts/python/freepy/globals.py
Normal file
14
scripts/python/freepy/globals.py
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
import os
|
||||
|
||||
if os.environ.has_key('FREEPY_DEBUG_ON'):
|
||||
# pull from environment if avail
|
||||
FREEPY_DEBUG_ON = os.environ['FREEPY_DEBUG_ON']
|
||||
else:
|
||||
# fall back to hardcoded value
|
||||
#FREEPY_DEBUG_ON = False
|
||||
FREEPY_DEBUG_ON = True
|
||||
|
||||
def debug(msg):
|
||||
if FREEPY_DEBUG_ON:
|
||||
print msg
|
92
scripts/python/freepy/loginrequest.sm
Normal file
92
scripts/python/freepy/loginrequest.sm
Normal file
@@ -0,0 +1,92 @@
|
||||
%start MainMap::Startup
|
||||
%class LoginRequest
|
||||
|
||||
%map MainMap
|
||||
%%
|
||||
|
||||
Startup
|
||||
{
|
||||
AuthRequest
|
||||
AuthRequestStarted
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
AuthRequestStarted
|
||||
{
|
||||
BlankLine
|
||||
AuthRequestFinished
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AuthRequestFinished
|
||||
{
|
||||
CommandReply
|
||||
CommandReplyStarted
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CommandReplyStarted
|
||||
{
|
||||
ReplyText
|
||||
GotReplyText
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
GotReplyText
|
||||
{
|
||||
BlankLine
|
||||
Startup
|
||||
{
|
||||
setRequestFinished(); callOrErrback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Default
|
||||
{
|
||||
BlankLine
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - Was not expecting a blank line");
|
||||
}
|
||||
|
||||
AuthRequest
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - Was not expecting auth request");
|
||||
}
|
||||
|
||||
CommandReply
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting a command reply");
|
||||
}
|
||||
|
||||
ReplyText
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting reply text");
|
||||
}
|
||||
|
||||
ProcessLine(line)
|
||||
nil
|
||||
{
|
||||
setRequestFinished();
|
||||
errbackDeferred("Protocol failure - was not expecting need to receive a line that needs to be processed");
|
||||
}
|
||||
|
||||
}
|
||||
%%
|
210
scripts/python/freepy/loginrequest_sm.py
Normal file
210
scripts/python/freepy/loginrequest_sm.py
Normal file
@@ -0,0 +1,210 @@
|
||||
# DO NOT EDIT.
|
||||
# generated by smc (http://smc.sourceforge.net/)
|
||||
# from file : loginrequest.sm
|
||||
|
||||
import statemap
|
||||
|
||||
|
||||
class LoginRequestState(statemap.State):
|
||||
|
||||
def Entry(self, fsm):
|
||||
pass
|
||||
|
||||
def Exit(self, fsm):
|
||||
pass
|
||||
|
||||
def AuthRequest(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def CommandReply(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def ProcessLine(self, fsm, line):
|
||||
self.Default(fsm)
|
||||
|
||||
def ReplyText(self, fsm):
|
||||
self.Default(fsm)
|
||||
|
||||
def Default(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write('TRANSITION : Default\n')
|
||||
msg = "\n\tState: %s\n\tTransition: %s" % (
|
||||
fsm.getState().getName(), fsm.getTransition())
|
||||
raise statemap.TransitionUndefinedException, msg
|
||||
|
||||
class MainMap_Default(LoginRequestState):
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.BlankLine()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - Was not expecting a blank line")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def AuthRequest(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.AuthRequest()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - Was not expecting auth request")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def CommandReply(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.CommandReply()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting a command reply")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def ReplyText(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ReplyText()\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting reply text")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
def ProcessLine(self, fsm, line):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Default.ProcessLine(line)\n")
|
||||
|
||||
endState = fsm.getState()
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.errbackDeferred("Protocol failure - was not expecting need to receive a line that needs to be processed")
|
||||
finally:
|
||||
fsm.setState(endState)
|
||||
|
||||
class MainMap_Startup(MainMap_Default):
|
||||
|
||||
def AuthRequest(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.Startup.AuthRequest()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.AuthRequestStarted)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_AuthRequestStarted(MainMap_Default):
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.AuthRequestStarted.BlankLine()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.AuthRequestFinished)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_AuthRequestFinished(MainMap_Default):
|
||||
|
||||
def CommandReply(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.AuthRequestFinished.CommandReply()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.CommandReplyStarted)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_CommandReplyStarted(MainMap_Default):
|
||||
|
||||
def ReplyText(self, fsm):
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.CommandReplyStarted.ReplyText()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.setState(MainMap.GotReplyText)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap_GotReplyText(MainMap_Default):
|
||||
|
||||
def BlankLine(self, fsm):
|
||||
ctxt = fsm.getOwner()
|
||||
if fsm.getDebugFlag() == True:
|
||||
fsm.getDebugStream().write("TRANSITION : MainMap.GotReplyText.BlankLine()\n")
|
||||
|
||||
fsm.getState().Exit(fsm)
|
||||
fsm.clearState()
|
||||
try:
|
||||
ctxt.setRequestFinished()
|
||||
ctxt.callOrErrback()
|
||||
finally:
|
||||
fsm.setState(MainMap.Startup)
|
||||
fsm.getState().Entry(fsm)
|
||||
|
||||
class MainMap:
|
||||
|
||||
Startup = MainMap_Startup('MainMap.Startup', 0)
|
||||
AuthRequestStarted = MainMap_AuthRequestStarted('MainMap.AuthRequestStarted', 1)
|
||||
AuthRequestFinished = MainMap_AuthRequestFinished('MainMap.AuthRequestFinished', 2)
|
||||
CommandReplyStarted = MainMap_CommandReplyStarted('MainMap.CommandReplyStarted', 3)
|
||||
GotReplyText = MainMap_GotReplyText('MainMap.GotReplyText', 4)
|
||||
Default = MainMap_Default('MainMap.Default', -1)
|
||||
|
||||
class LoginRequest_sm(statemap.FSMContext):
|
||||
|
||||
def __init__(self, owner):
|
||||
statemap.FSMContext.__init__(self)
|
||||
self._owner = owner
|
||||
self.setState(MainMap.Startup)
|
||||
MainMap.Startup.Entry(self)
|
||||
|
||||
def AuthRequest(self):
|
||||
self._transition = 'AuthRequest'
|
||||
self.getState().AuthRequest(self)
|
||||
self._transition = None
|
||||
|
||||
def BlankLine(self):
|
||||
self._transition = 'BlankLine'
|
||||
self.getState().BlankLine(self)
|
||||
self._transition = None
|
||||
|
||||
def CommandReply(self):
|
||||
self._transition = 'CommandReply'
|
||||
self.getState().CommandReply(self)
|
||||
self._transition = None
|
||||
|
||||
def ProcessLine(self, *arglist):
|
||||
self._transition = 'ProcessLine'
|
||||
self.getState().ProcessLine(self, *arglist)
|
||||
self._transition = None
|
||||
|
||||
def ReplyText(self):
|
||||
self._transition = 'ReplyText'
|
||||
self.getState().ReplyText(self)
|
||||
self._transition = None
|
||||
|
||||
def getState(self):
|
||||
if self._state == None:
|
||||
raise statemap.StateUndefinedException
|
||||
return self._state
|
||||
|
||||
def getOwner(self):
|
||||
return self._owner
|
||||
|
81
scripts/python/freepy/models.py
Normal file
81
scripts/python/freepy/models.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
|
||||
Version: MPL 1.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the
|
||||
License.
|
||||
|
||||
The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Anthony Minessale II <anthmct@yahoo.com>
|
||||
Portions created by the Initial Developer are Copyright (C)
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s): Traun Leyden <tleyden@branchcut.com>
|
||||
"""
|
||||
|
||||
"""
|
||||
Data models for objects inside freeswitch
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
class ConfMember:
|
||||
|
||||
def __init__(self, rawstring):
|
||||
self.rawstring = rawstring
|
||||
self.member_id = None
|
||||
self.member_uri = None
|
||||
self.uuid = None
|
||||
self.caller_id_name = None
|
||||
self.caller_id_number = None
|
||||
self.flags = None
|
||||
self.volume_in = None
|
||||
self.volume_out = None
|
||||
self.energy_level = None
|
||||
|
||||
self.parse(self.rawstring)
|
||||
|
||||
def parse(self, rawstring):
|
||||
"""
|
||||
1;sofia/mydomain.com/user@somewhere.com;898e6552-24ab-11dc-9df7-9fccd4095451;FreeSWITCH;0000000000;hear|speak;0;0;300
|
||||
"""
|
||||
fields = rawstring.split(";")
|
||||
self.member_id = fields[0]
|
||||
self.member_uri = fields[1]
|
||||
self.uuid = fields[2]
|
||||
self.caller_id_name = fields[3]
|
||||
self.caller_id_number = fields[4]
|
||||
self.flags = fields[5]
|
||||
self.volume_in = fields[6]
|
||||
self.volume_out = fields[7]
|
||||
self.energy_level = fields[8]
|
||||
|
||||
def brief_member_uri(self):
|
||||
"""
|
||||
if self.member_uri is sofia/mydomain.com/foo@bar.com
|
||||
return foo@bar.com
|
||||
"""
|
||||
if not self.member_uri:
|
||||
return None
|
||||
|
||||
if self.member_uri.find("/") == -1:
|
||||
return self.member_uri
|
||||
r = self.member_uri.split("/")[-1] # tokenize on "/" and return last item
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def __str__(self):
|
||||
return "%s (%s)" % (self.member_id, self.member_uri)
|
364
scripts/python/freepy/request.py
Normal file
364
scripts/python/freepy/request.py
Normal file
@@ -0,0 +1,364 @@
|
||||
"""
|
||||
FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
|
||||
Version: MPL 1.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the
|
||||
License.
|
||||
|
||||
The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
Anthony Minessale II <anthmct@yahoo.com>
|
||||
Portions created by the Initial Developer are Copyright (C)
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s): Traun Leyden <tleyden@branchcut.com>
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.protocols.basic import LineReceiver
|
||||
from twisted.internet.protocol import Protocol, ClientFactory
|
||||
from twisted.python import failure
|
||||
from twisted.python.failure import Failure
|
||||
import time, re
|
||||
from time import strftime
|
||||
from Queue import Queue
|
||||
|
||||
from freepy import models
|
||||
import freepy.globals
|
||||
from freepy.globals import debug
|
||||
|
||||
"""
|
||||
These are response handlers for different types of requests.
|
||||
It reads the response from freeswitch, and calls back
|
||||
self.deferred with the result.
|
||||
|
||||
The naming could be improved, but here is the translation:
|
||||
|
||||
LoginRequest - Response handler for a login request
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class FreepyRequest(object):
|
||||
|
||||
def __init__(self):
|
||||
self.deferred = defer.Deferred()
|
||||
self.response_content = ""
|
||||
self.finished = False
|
||||
|
||||
def isRequestFinished(self):
|
||||
return self.finished
|
||||
|
||||
def setRequestFinished(self):
|
||||
debug("setRequestFinished called. response_content: %s " %
|
||||
self.response_content)
|
||||
self.finished = True
|
||||
|
||||
def getDeferred(self):
|
||||
return self.deferred
|
||||
|
||||
def callbackDeferred(self, cbval):
|
||||
self.deferred.callback(cbval)
|
||||
|
||||
def errbackDeferred(self, result):
|
||||
self.deferred.errback(Exception(str(result)))
|
||||
|
||||
def process(self, line):
|
||||
"""
|
||||
processs a line from the fs response. if the fs response has been
|
||||
detected to be finished, then:
|
||||
|
||||
* create an appropriate response based on request type
|
||||
* callback deferred with response
|
||||
* rturn True to indicate we are finished
|
||||
|
||||
otherwise, if the fs response is incomplete, just buffer the data
|
||||
"""
|
||||
if not line.strip() or len(line.strip()) == 0:
|
||||
self._fsm.BlankLine()
|
||||
return self.isRequestFinished()
|
||||
|
||||
matchstr = re.compile("auth/request", re.I)
|
||||
result = matchstr.search(line)
|
||||
if (result != None):
|
||||
self._fsm.AuthRequest()
|
||||
return self.isRequestFinished()
|
||||
|
||||
|
||||
matchstr = re.compile("command/reply", re.I)
|
||||
result = matchstr.search(line)
|
||||
if (result != None):
|
||||
self._fsm.CommandReply()
|
||||
return self.isRequestFinished()
|
||||
|
||||
|
||||
matchstr = re.compile("Reply-Text", re.I)
|
||||
result = matchstr.search(line)
|
||||
if (result != None):
|
||||
debug("FREEPY: got Reply-Text")
|
||||
fields = line.split(":") # eg, ['Reply-Text','+OK Job-UUID', '882']
|
||||
endfields = fields[1:]
|
||||
self.response_content = "".join(endfields)
|
||||
self._fsm.ReplyText()
|
||||
return self.isRequestFinished()
|
||||
|
||||
matchstr = re.compile("Job-UUID", re.I)
|
||||
result = matchstr.search(line)
|
||||
if (result != None):
|
||||
fields = line.split(":") # eg, ['Job-UUID','c9eee07e-508-..']
|
||||
endfields = fields[1:]
|
||||
# ignore job uuid given on this line, take the one sent
|
||||
# in Reply-Text response line
|
||||
# self.response_content = "".join(endfields)
|
||||
self._fsm.JobUuid()
|
||||
return self.isRequestFinished()
|
||||
|
||||
matchstr = re.compile("api/response", re.I)
|
||||
result = matchstr.search(line)
|
||||
if (result != None):
|
||||
self._fsm.ApiResponse()
|
||||
return self.isRequestFinished()
|
||||
|
||||
matchstr = re.compile("Content-Length", re.I)
|
||||
result = matchstr.search(line)
|
||||
if (result != None):
|
||||
# line: Content-Length: 34
|
||||
self.content_length = int(line.split(":")[1].strip())
|
||||
self._fsm.ContentLength()
|
||||
return self.isRequestFinished()
|
||||
|
||||
self._fsm.ProcessLine(line)
|
||||
return self.isRequestFinished()
|
||||
|
||||
def callOrErrback(self):
|
||||
matchstr = re.compile("OK", re.I)
|
||||
result = matchstr.search(self.response_content)
|
||||
if (result != None):
|
||||
self.callbackDeferred(self.response_content)
|
||||
return
|
||||
self.errbackDeferred(Failure(Exception(self.response_content)))
|
||||
|
||||
def doNothing(self):
|
||||
# weird smc issue workaround attempt
|
||||
pass
|
||||
|
||||
|
||||
class LoginRequest(FreepyRequest):
|
||||
"""
|
||||
Example success response
|
||||
========================
|
||||
|
||||
lineReceived: Content-Type: auth/request
|
||||
lineReceived:
|
||||
lineReceived: Content-Type: command/reply
|
||||
lineReceived: Reply-Text: +OK accepted
|
||||
lineReceived:
|
||||
|
||||
Example failure response
|
||||
========================
|
||||
|
||||
lineReceived: Content-Type: auth/request
|
||||
lineReceived:
|
||||
lineReceived: Content-Type: command/reply
|
||||
lineReceived: Reply-Text: -ERR invalid
|
||||
lineReceived:
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(LoginRequest, self).__init__()
|
||||
import loginrequest_sm
|
||||
self._fsm = loginrequest_sm.LoginRequest_sm(self)
|
||||
|
||||
def callOrErrback(self):
|
||||
matchstr = re.compile("OK", re.I)
|
||||
result = matchstr.search(self.response_content)
|
||||
if (result != None):
|
||||
self.callbackDeferred(self.response_content)
|
||||
return
|
||||
msg = "Login failed, most likely a bad password"
|
||||
self.errbackDeferred(Failure(Exception(msg)))
|
||||
|
||||
def getReplyText(self):
|
||||
self.response_content
|
||||
|
||||
|
||||
class BgApiRequest(FreepyRequest):
|
||||
|
||||
"""
|
||||
Here is one of the 'bgapi requests' this class
|
||||
supports:
|
||||
|
||||
|
||||
linereceived: Content-Type: command/reply
|
||||
linereceived: Reply-Text: +OK Job-UUID: 788da080-24e0-11dc-85f6-3d7b12..
|
||||
linereceived:
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
super(BgApiRequest, self).__init__()
|
||||
import bgapirequest_sm
|
||||
self._fsm = bgapirequest_sm.BgApiRequest_sm(self)
|
||||
|
||||
|
||||
def getResponse(self):
|
||||
|
||||
# subclasses may want to parse this into a meaningful
|
||||
# object or set of objects (eg, see ListConfRequest)
|
||||
# By default, just return accumulated string
|
||||
return self.response_content
|
||||
|
||||
|
||||
|
||||
class ApiRequest(FreepyRequest):
|
||||
|
||||
"""
|
||||
Here is one of the 'api requests' this class
|
||||
supports:
|
||||
|
||||
lineReceived: Content-Type: api/response
|
||||
lineReceived: Content-Length: 34
|
||||
lineReceived:
|
||||
lineReceived: Call Requested: result: [SUCCESS]
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(ApiRequest, self).__init__()
|
||||
import apirequest_sm
|
||||
self._fsm = apirequest_sm.ApiRequest_sm(self)
|
||||
self.response_content = ""
|
||||
|
||||
|
||||
def doNothing(self):
|
||||
# weird smc issue workaround attempt
|
||||
pass
|
||||
|
||||
def add_content(self, line):
|
||||
"""
|
||||
Add content to local buffer
|
||||
return - True if finished adding content, False otherwise
|
||||
"""
|
||||
|
||||
# since the twisted LineReceiver strips off the newline,
|
||||
# we need to add it back .. otherwise the Content-length
|
||||
# will be off by one
|
||||
line += "\n"
|
||||
|
||||
self.response_content += line
|
||||
if len(self.response_content) == self.content_length:
|
||||
return True
|
||||
elif len(self.response_content) > self.content_length:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def getResponse(self):
|
||||
|
||||
# subclasses may want to parse this into a meaningful
|
||||
# object or set of objects (eg, see ListConfRequest)
|
||||
# By default, just return accumulated string
|
||||
return self.response_content
|
||||
|
||||
|
||||
class DialoutRequest(ApiRequest):
|
||||
"""
|
||||
Example raw dialout response
|
||||
============================
|
||||
|
||||
lineReceived: Content-Type: api/response
|
||||
lineReceived: Content-Length: 34
|
||||
lineReceived:
|
||||
lineReceived: Call Requested: result: [SUCCESS]
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(DialoutRequest, self).__init__()
|
||||
|
||||
|
||||
class BgDialoutRequest(BgApiRequest):
|
||||
def __init__(self):
|
||||
super(BgDialoutRequest, self).__init__()
|
||||
|
||||
|
||||
class ConfKickRequest(ApiRequest):
|
||||
"""
|
||||
Example response
|
||||
================
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(ConfKickRequest, self).__init__()
|
||||
|
||||
class BgConfKickRequest(BgApiRequest):
|
||||
"""
|
||||
Example response
|
||||
================
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(BgConfKickRequest, self).__init__()
|
||||
|
||||
|
||||
class ListConfRequest(ApiRequest):
|
||||
"""
|
||||
Response to request to list conferences:
|
||||
========================================
|
||||
|
||||
lineReceived: Content-Type: api/response
|
||||
lineReceived: Content-Length: 233
|
||||
lineReceived:
|
||||
lineReceived: 2;sofia/mydomain.com/foo@bar.com;e9be6e72-2410-11dc-8daf-7bcec6dda2ae;FreeSWITCH;0000000000;hear|speak;0;0;300
|
||||
lineReceived: 1;sofia/mydomain.com/foo2@bar.com;e9be5fcc-2410-11dc-8daf-7bcec6dda2ae;FreeSWITCH;0000000000;hear|speak;0;0;300
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(ListConfRequest, self).__init__()
|
||||
self.conf_members = []
|
||||
|
||||
def add_content(self, line):
|
||||
"""
|
||||
conf not empty example
|
||||
======================
|
||||
1;sofia/mydomain.com/888@conference.freeswitch.org;898e6552-24ab-11dc-9df7-9fccd4095451;FreeSWITCH;0000000000;hear|speak;0;0;300
|
||||
|
||||
conf empty example
|
||||
==================
|
||||
Conference foo not found
|
||||
"""
|
||||
|
||||
matchstr = re.compile("not found", re.I)
|
||||
result = matchstr.search(line)
|
||||
if (result != None):
|
||||
# no conf found..
|
||||
pass
|
||||
else:
|
||||
confmember = models.ConfMember(line)
|
||||
self.conf_members.append(confmember)
|
||||
|
||||
return super(ListConfRequest, self).add_content(line)
|
||||
|
||||
def getResponse(self):
|
||||
|
||||
# TODO: parse this content into a meaningful
|
||||
# 'object' .. though, not sure this is really
|
||||
# necessary. wait till there's a need
|
||||
return self.conf_members
|
||||
|
Reference in New Issue
Block a user