forked from Mirrors/freeswitch
6e02348c36
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@6063 d0543943-73ff-0310-b7d9-9358b9ac24b2
533 lines
16 KiB
Python
533 lines
16 KiB
Python
#!/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()
|