123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- """
- FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
- Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
- 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 <anthm@freeswitch.org>
- 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
|